npm init vite@latest my-project
cd my-project
npm install
npm run dev
npm install vue-router@next --save
import { createRouter,Router, createWebHistory, RouteRecordRaw } from 'vue-router';
const history = createWebHistory()
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: '/index',
component: () => import('../views/Index.vue'),
meta:{
title:'首页'
}
},
];
const router: Router = createRouter({
history:createWebHistory('/'),
routes,
})
export default router
在main.ts中引入router
import router from './router/index'
createApp(App).use(router).mount('#app')
将App.vue中内容替换
<router-view />
npm install --save-dev sass
就这么一句安装就可以使用了,用vuecli的时候,还要安装sass-loader、node-sass等,但是vite只需要安装sass就可以了
npm install pinia@next
数据持久化,可使用插件pinia-plugin-persist
npm install pinia-plugin-persist -S
import {createPinia} from "pinia"
//需要注意的是从pinia中结构出来的createPinia是一个函数,挂载需要先调用执行
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)
app.use(createPinia())
// index.js
import {defineStore} from "pinia"
export const useAppStore = defineStore("app",{
state:()=>{
return{
count:1,
number:1
}
},
getters:{
bdCount(){
return this.count*2
}
},
actions:{
increment(num){
this.count=Math.round(100 * Math.random()*num)
}
},
persist: {
enabled: true,
//通过修改配置项,改成localStorage
strategies: [
{
storage: localStorage,
//paths: ["count"], //可以通过paths字段指定需要缓存的数据,如不指定paths,则默认是缓存所有
},
],
}
})
pinia使用
<template>
<NavbarHeader :msg="msg" />
我是index里面的值:{{count}} <br /><br />
我是getter里面的值:{{bdCount}}<br />
<button @click="change1">修改store.name</button><br />
<button @click="reset">reset</button><br />
<button @click="moreEdit">批量修改state数据</button><br />
<button @click="fatherAction">父组件里面调用action</button><br />
</template>
<script setup lang="ts">
import NavbarHeader from '@/components/index/NavbarHeader.vue'
import { ref, reactive } from 'vue'
import { storeToRefs } from 'pinia'
import { useAppStore } from '@/store/index.js'
const store = useAppStore()
const { count } = storeToRefs(store)
const { bdCount } = storeToRefs(store)
let change1=()=>{store.count++}
let reset=()=>{store.$reset()}
let moreEdit=()=>{
store.$patch({
count:99,
number:99
})
}
let fatherAction=()=>{store.increment(2)}
let msg=ref('大家好才是真的好')
</script>
<style lang="scss" scoped>
</style>
npm install axios
import Axios from 'axios'
createApp( App ).config.globalProperties.Axios = Axios
import axios, {AxiosInstance, AxiosRequestConfig} from 'axios'
import { $serverUrl } from "../config"
class HttpRequest {
private readonly baseUrl: string;
constructor() {
this.baseUrl = $serverUrl
}
getInsideConfig() {
const config = {
baseURL: this.baseUrl,
headers: {
//
}
}
return config
}
// 请求拦截
interceptors(instance: AxiosInstance, url: string | number | undefined) {
instance.interceptors.request.use(config => {
// 添加全局的loading..
// 请求头携带token
return config
}, (error: any) => {
return Promise.reject(error)
})
//响应拦截
instance.interceptors.response.use(res => {
//返回数据
const {data} = res
return data
}, (error: any) => {
console.log('error==>',error)
return Promise.reject(error)
})
}
request(options: AxiosRequestConfig) {
const instance = axios.create()
options = Object.assign(this.getInsideConfig(), options)
this.interceptors(instance, options.url)
return instance(options)
}
}
const http = new HttpRequest()
export default http
import http from '../utils/request'
export function getFileList() {
return http.request({
url: '/lencon/pc/index/school_type',
method: 'post'
})
}
export function saveSysEventType (parameter:any) {
return http.request({
url: '/lencon/pc/index/titleImg',
method: 'post',
data: parameter,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
页面请求编写
<script setup lang='ts'>
import { getFileList, saveSysEventType} from '../apis/index'
import { ref, isRef, reactive,onMounted } from 'vue'
interface IndexData{
dirct:any
imgUrl:string
}
const data = reactive<IndexData>({
dirct:[],
imgUrl:''
})
onMounted(() => {
dirct()
dircts()
});
const dirct = ()=>{
getFileList().then((res:any) => {
data.dirct = res.data
})
}
const dircts = ()=>{
let datas={id:9}
saveSysEventType(datas).then((res:any) => {
data.imgUrl = res.res
})
}
</script>
在src根目录下创建一个后缀为env.d.ts的文件
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '@/store/index.js'
完整vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//npm install @types/node --save-dev
import path from 'path'
//vite打包性能优化之开启Gzip压缩
import viteCompression from "vite-plugin-compression"; //npm i vite-plugin-compression -D
export default defineConfig({
server:{
host: '0.0.0.0',
port: 3000,
strictPort: false, // 若3000端口被占用,是否直接结束项目
https: false, // 是否开启 https
open: true, // 是否自动在浏览器打开
proxy: {
"/api": {
target: "http://pro.dangjian.link",
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, // websocket是否支持
rewrite: (path) => path.replace(/^\/api/, ""),
},
}
},
build: {
target: "es2020", //指定es版本,浏览器的兼容性
outDir: "dist",
assetsDir: "assets", // 指定静态资源存放路径
// cssCodeSplit: true, // css代码拆分,禁用则所有样式保存在一个css里面
sourcemap: false, // 是否构建source map 文件
// manifest: true, // 是否在outDir中生成 manifest.json
rollupOptions: {
// input: '/path/to/main.ts' // 覆盖默认的 .html 入口
},
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
}
},
resolve: {
// 配置项目路径别名,在开发时不需要写完整的路径名称,需要安装 @types/node,执行命令npm i -D @types/node --save-dev即可
alias: {
"@": path.resolve(__dirname, "src"),
"@assets": path.resolve(__dirname, "src/assets"),
"@components": path.resolve(__dirname, "src/components"),
},
},
optimizeDeps: {
include: [],
},
plugins: [
vue(),
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: "gzip",
ext: ".gz",
})
]
})
npm install animate.css --save
import 'animate.css';
<div class="animated swing">Example</div>
Animate.css官网
https://animate.style/
npm i hover.css --save
import 'hover.css'
<div class="hvr-bounce-to-left">Example</div>
hover.css官网
http://ianlunn.github.io/Hover/
main.ts
const checkImg = (value:string) => {
if (!value) return ''
if(value.substring(0,4)!='http'){
return $serverUrl+value;
}else{
return value;
}
}
app.config.globalProperties.$checkImg = checkImg
组件内使用
import { getCurrentInstance } from 'vue'
const { $checkImg } = currentInstance.appContext.config.globalProperties
<img :src="$checkImg(item.news_headpic)" alt="">
npm install element-plus --save
html 引入
<link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
<!-- Import Vue 3 -->
<script src="//unpkg.com/vue@3"></script>
<!-- Import component library -->
<script src="//unpkg.com/element-plus"></script>
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
安装icon
npm install @element-plus/icons-vue
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
按需导入需要再继续安装两个插件
npm install -D unplugin-vue-components unplugin-auto-import
在项目的.config.ts中引入并配置plugins
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
正常使用icon
import * as Icons from "@element-plus/icons-vue";
Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});
<el-icon><component is="Loading"></component></el-icon>
按需引入icon
安装插件
npm install -D unplugin-auto-import unplugin-icons
main可以去掉全局引入
// import * as Icons from "@element-plus/icons-vue";
const app = createApp(App)
// Object.keys(Icons).forEach((key) => {app.component(key, Icons[key as keyof typeof Icons]);});
vite.config.ts中修改
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
plugins: [
vue(),
AutoImport({
// Auto import functions from Vue, e.g. ref, reactive, toRef...
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue'],
// Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style)
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
ElementPlusResolver(),
// Auto import icon components
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
}),
],
dts: path.resolve(path.resolve(__dirname,'src'), 'auto-imports.d.ts'),
}),
Components({
resolvers: [
// Auto register icon components
// 自动注册图标组件
IconsResolver({
enabledCollections: ['ep'],
}),
// Auto register Element Plus components
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: path.resolve(path.resolve(__dirname,'src'), 'components.d.ts'),
}),
Icons({
autoInstall: true,
}),
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
vueSetupExtend (),
]
页面使用
<el-icon><i-ep-circle-check-filled /></el-icon>
npm i --save ant-design-vue
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
const app = createApp(App);
app.use(Antd).mount('#app');
如果你使用的 Vite,你可以使用 unplugin-vue-components 来进行按需加载。但是此插件无法处理非组件模块,如 message,这种组件需要手动加载:
import { message } from 'ant-design-vue';
import 'ant-design-vue/es/message/style/css'; //vite只能用 ant-design-vue/es 而非 ant-design-vue/lib
npm install swiper@6 --save
import SwiperCore, {Autoplay,Pagination} from 'swiper';
import {Swiper,SwiperSlide} from 'swiper/vue';
import 'swiper/swiper.min.css';
import 'swiper/components/pagination/pagination.scss';
SwiperCore.use([Autoplay,Pagination]);
export default {
components: {
Vue3SeamlessScroll,Swiper, SwiperSlide
},
setup () {
const store = useStore()
let swiper_options = reactive({
autoplay:{
delay:8000,
disableOnInteraction: true
},
loop:true,
speed:1000,
pagination:{
clickable: true
}
})
const data = reactive<Table>({
tableData:[],
page:1,
totalPages:1,
deptId: computed(() => {
return store.getters.deptId
}),
async getData () {
let { deptId } = data
let res = await Axios.post("/lencon/bigData/learning/problem", { deptId,page:data.page,pageSize:5 })
if ( res.data.code == 1 ) {
data.tableData = res.data.res
data.totalPages= res.data.totalPages
}
},
})
const onSwiper = (swiper:any) => {
// if(data.page==data.totalPages){
// data.page=1
// }else{
// data.page+=1
// }
// data.getData()
};
const onSlideChange = () => {
if(data.page==data.totalPages){
data.page=1
}else{
data.page+=1
}
data.getData()
};
watch(() => data.deptId, () => {
data.getData()
})
// 初始化表格
onMounted(() => {
data.getData()
})
return {
...toRefs( data ),
swiper_options,onSwiper,onSlideChange
}
}
npm install vue3-seamless-scroll --save
import vue3SeamlessScroll from 'vue3-seamless-scroll';
const app = createApp(App);
app.use(vue3SeamlessScroll)
npm i echarts --save
使用 可以采用这位大佬的笔记 懒得写了 哈哈
https:///sunddy_x/article/details/124432548
补充 vite 引入文件
{ iconClass: new URL('@/assets/images/index/index_right_2.png', import.meta.url).href}
npm i vite-plugin-vue-setup-extend -D
在vite.config.ts配置
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
plugins: [
vueSetupExtend ()
]
<script lang="ts" setup name="自定义name">
</script>
完整的main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import Login from '@/components/common/Login.vue'
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css'
import 'animate.css'
import 'hover.css'
import {createPinia} from "pinia"
import piniaPersist from 'pinia-plugin-persist'
const store =createPinia()
store.use(piniaPersist)
import Axios from 'axios'
import { $serverUrl } from "./config"
const checkImg = (value:string) => {
if (!value) return ''
if(value.substring(0,4)!='http'){
return $serverUrl+value;
}else{
return value;
}
}
const app = createApp(App)
app.config.globalProperties.$serverUrl = $serverUrl
app.config.globalProperties.Axios = Axios
app.config.globalProperties.$checkImg = checkImg
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.component('Login',Login).use(ElementPlus).use(router).use(store).mount('#app')
可能是TS升级到5.x带来的规范性问题
把tsconfig.json配置项moduleResolution:"bundler"改为 moduleResolution:"node"就可以了
("moduleResolution": "node" 表示 模块化查找的时候按照nodejs方式进行查找。"moduleResolution": "bundler" 表示 打包工具的模块解析策略来查找。 TS升级到5.x,默认"moduleResolution": "bundler" )
父子向子组件传值
父组件
<navbar-header ref="navbarheader" :jsonData="data.dirct"
@editclick="editclick" @emitclick="emitclick" @removeclick="removeclick">
</navbar-header>
子组件
defineProps<{
jsonData:any
}>();
子组件向父组件传值
子组件
const emit = defineEmits(['addclick','removeclick','editclick'])
const addFather = ()=>{
emit('addclick', {name: "干部培训666",type:22} )
}
const removeFather = (e:String,index:number)=>{
emit('removeclick', {id:e,type:index} )
}
const editFather = (e:object,index:number)=>{
emit('editclick', {...e,index:index} )
}
父组件
const addclick = (obj:object)=>{
data.dirct.push(obj)
}
const removeclick = (e:any)=>{
data.dirct.splice(e.type,1)
}
const editclick = (e:any)=>{
data.dirct[e.index].name='干部培训a:'+e.type
}
父组件获取子组件内的值及修改子组件的值
父组件
我是子组件内的值:{{data.sondata}}
<el-button type="info" mini @click="editSonData">获取子组件值</el-button>
let navbarheader = ref()
const getSonData= ()=>{
data.sondata=navbarheader.value.sonName
}
const editSonData= ()=>{
navbarheader.value.editSon(5)
}
子组件(切记一定要defineExpose出来)
const counts = ref(0)
const editSon = (e:number)=>{
counts.value+=e
}
const sonName = ref("JackSon");
defineExpose({
sonName,
editSon
})
完整代码
父组件
<template>
<img :src="data.imgUrl" draggable="false" />
<br />
<navbar-header ref="navbarheader" :jsonData="data.dirct"
@editclick="editclick" @addclick="addclick" @removeclick="removeclick">
</navbar-header>
<hr />
我是子组件内的值:{{data.sondata}}
<el-button type="info" mini @click="editSonData">获取子组件值</el-button>
</template>
<script setup lang='ts'>
import { getFileList, saveSysEventType} from '../apis/index'
import { ref, isRef, reactive,onMounted } from 'vue'
import NavbarHeader from '@/components/index/NavbarHeader.vue'
interface IndexData{
dirct:any
imgUrl:string
sondata:any
}
const data = reactive<IndexData>({
dirct:[],
imgUrl:'',
sondata:{},
})
onMounted(() => {
dirct()
dircts()
getSonData()
});
let navbarheader = ref()
const getSonData= ()=>{
data.sondata=navbarheader.value.sonName
}
const editSonData= ()=>{
navbarheader.value.editSon(5)
}
const dirct = ()=>{
getFileList().then((res:any) => {
data.dirct = res.data
})
}
const dircts = ()=>{
let datas={id:9}
saveSysEventType(datas).then((res:any) => {
data.imgUrl = res.res
})
}
const addclick = (obj:object)=>{
data.dirct.push(obj)
}
const removeclick = (e:any)=>{
data.dirct.splice(e.type,1)
}
const editclick = (e:any)=>{
data.dirct[e.index].name='干部培训a:'+e.type
}
</script>
<style lang='scss' scoped>
</style>
子组件
<template>
<div class="card">
<button type="button" @click="counts++">我是父组件改变的值:{{ counts }}</button>
</div>
<div class="item" v-for="(item ,index) in jsonData" :key="index">
{{item.name}}
<el-button type="success" @click="editFather(item,index)">修改父组件的值</el-button>
<el-button type="danger" @click="removeFather(item.name,index)">删除父组件的值</el-button>
</div><br />
<el-button type="primary" @click="addFather">增加父组件的值</el-button><br />
<br />
</template>
<script setup lang="ts">
import { ref,isRef } from 'vue'
defineProps<{
jsonData:any
}>();
const counts = ref(0)
const emit = defineEmits(['addclick','removeclick','editclick'])
const addFather = ()=>{
emit('addclick', {name: "干部培训666",type:22} )
}
const removeFather = (e:String,index:number)=>{
emit('removeclick', {id:e,type:index} )
}
const editFather = (e:object,index:number)=>{
emit('editclick', {...e,index:index} )
}
const editSon = (e:number)=>{
counts.value+=e
}
const sonName = ref("JackSon");
defineExpose({
sonName,
editSon
})
</script>
<style scoped>
.read-the-docs {
color: #888;
}
.item{
margin: 10px;
}
</style>
补充(兄弟组件传值)
1.安装mitt
npm install --save mitt
2.创建一个js文件 比如 utils/bus.ts
import mitt from 'mitt'
const emiiter = mitt()
export default emiiter
3.组件中使用
组件A传值
import emitter from '../../utils/bus'
emitter.emit('handleEvent', ref({show:true}))
组件B收值
import { ref, reactive, onMounted, computed, watch, onBeforeMount, onUnmounted } from 'vue'
import emitter from '../../utils/bus'
onBeforeMount(() => {
// 开启监听,监听到handleEvent事件后,执行handleEvent函数
emitter.on('handleEvent',handleEvent)
})
const handleEvent = (Aname:any)=>{
navData.NeedLogin = Aname.value.show
}
onUnmounted(() => {
emitter.off('handleEvent',handleEvent)// 取消handleEvent事件的handelEventFn函数监听
emitter.off('handleEvent')// 取消handleEvent事件的全部处理函数的监听
})
多层组件传值(Provide / Inject)
import { provide, ref } from'vue'
let flag = ref<number>(1)
provide('flag', flag)
子组件接收
import { inject, Ref, ref } from 'vue'
const flag = inject<Ref<number>>('flag', ref(1))
修改值
const change = () => {
flag.value = 2
}
购物车案例
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="名字" width="180" />
<el-table-column label="数量">
<template #default="scope">
<el-input-number v-model="scope.row.num" :min="1" :max="10" @change="handleChange" />
</template>
</el-table-column>
<el-table-column prop="price" label="价格(元)" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">Delete</el-button>
</template>
</el-table-column>
</el-table>
<div>合计:{{total}}</div> <br />
<div>合计:{{token}}</div><br />
<el-button type="primary" plain @click="setToken">pinia设置token</el-button><br />
<el-button type="danger" plain @click="remToken">pinia清除token</el-button>
</template>
<script setup lang='ts'>
import { ref, reactive, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { storeToRefs } from 'pinia'
import { useAppStore } from '@/store/index.js'
import { saveSysEventType} from '../apis/index'
const store = useAppStore()
interface User {
name: string
num: number
price: number
}
const tableData = reactive<User[]>([
{name: "袜子",num: 1,price: 10},
{name: "鞋",num: 1,price: 100},
{name: "裙子",num: 1,price: 200},
{name: "裤子",num: 1,price: 150},
])
const handleEdit = (index: number, row: User) => {
ElMessage({
message: '你点击了修改按钮: '+row.name,
type: 'success',
})
}
const handleDelete = (index: number, row: User) => {
tableData.splice(index,1)
}
let total = ref<number>(0)
total = computed<number>(() => {
return tableData.reduce((prev, next) => {
return prev + (next.num * next.price)
}, 0)
})
const handleChange = (value: number) => {
}
const token=computed(()=>{
return storeToRefs(store).token.value
})
const setToken= ()=>{
store.setToken(20)
dircts()
}
const remToken = ()=>{
store.setToken(0)
}
const dircts = ()=>{
let datas={id:9}
saveSysEventType(datas).then((res:any) => {
console.log(res)
})
}
</script>
<style lang='scss' scoped>
</style>
watch
watch(token,(newVal,oldVal)=>{
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
},{
immediate:true,
deep:true
})
watch([total,token],(newVal,oldVal)=>{
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
},{
immediate:true
})
immediate的值为true时表示非惰性的立即执行的(默认情况下是false)
deep深层次触发(此处设置deep无意义)
监视所定义的响应式数据和多个响应式数据
监听reactive对象
1.若监视的是reactive定义的响应式数据,则无法正确获得oldValue
2.若监视的是reactive定义的响应式数据,则watch会强制开启深度监视
let person = reactive({
name:'张三',
age:'18',
job:{
j1:{
salary:20
}
}
})
watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
watch(()=>person.age,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
})
//从上边我们发现改变name,age都会触发监听,但是改变job不会
//这是因为name和age属性的值只是一个简单的基本类型数据,
//而job属性的值是一个对象,比较深,想要监视到,就要开启深度监视,程序如下:
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job改变了',newValue,oldValue)
},{deep:true})//此时job改变,会被监视到,此处的deep配置生效
//需要和情况三进行区分,此处watch监视的是reactive所定义的对象中的某个属性,而情况三watch监视的是reactive所定义的对象
//情况五:监视reactive所定义的响应式数据中的某些属性,写成数组的形式
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age改变了',newValue,oldValue)
})
watchEffect立即执行,没有惰性
watchEffect函数内部所指定的回调中用到的数据只要发生变化,就会重新执行回调
watchEffect(()=>{
console.log('新的值----', token.value);
})
对比 | 是否有惰性 | 参数 | 获得值 |
watch | 有惰性,数值再次改变后执行监听函数 | 可以侦听多个数据的变化 | 参数可以拿到当前值和原始值 |
watchEffect | 立即执行没有惰性 | 不需要传递侦听内容,自动感知代码依赖 | 不需要传递到很多参数,不能获取原始值 |
onBeforeMount()
在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。
onMounted()
在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
onBeforeUpdate()
数据更新时调用,发生在虚拟 DOM 打补丁之前。
onUpdated()
DOM更新后,updated的方法即会调用。
onBeforeUnmount()
在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted()
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
全局组件的使用
递归组件的使用
父组件
<template>
<tree-data :data="data" @edit-father="editFather"></tree-data>
</template>
<script setup lang='ts'>
import TreeData from '@/components/common/TreeData.vue'
import { ref, reactive } from 'vue'
type treeData = {
name: string,
icon?: string,
children?: treeData[] | []
}
const data = reactive<treeData[]>([
{
name: '陕西省',
children: [
{
name: '西安市',
children: [
{
name: '长安区',
children: [
{
name: '郭杜镇'
},
{
name: '大学城'
},
]
},
{
name: '雁塔区'
},
{
name: '高新区',
children: [
{
name: '科技园'
},
]
}
]
},
]
},
{
name: '四川省',
children: [
{
name: '成都市',
children: [
{
name: '高新区'
}
]
},
]
},
{
name: '北京市',
children: []
}
])
const editFather = (item:treeData)=>{
console.log(item);
}
</script>
<style lang='scss' scoped>
</style>
子组件
<template>
<div>
<div v-for="(item, index) in data" :key="index">
<div style="margin-left: 20px;cursor: pointer;" @click.stop="handleTreeItem(item)">
{{ item.name }}
<TreeData @edit-father="handleTreeItem" v-if="item?.children?.length" :data="item.children"></TreeData>
</div>
</div>
</div>
</template>
<script setup lang='ts'>
defineProps<{
data:any
}>()
type treeData = {
name: string;
icon?: string;
children?: treeData[] | [];
};
const emit = defineEmits(['edit-father'])
const handleTreeItem=(item:treeData)=>{
emit('edit-father',item)
}
</script>
<style lang='scss' scoped>
</style>
动态组件
<template>
<ul class="tab_list">
<li :class="active==index?'active':''" @click="changeComponent(item,index)" v-for="(item, index) in data" :key="index">
{{ item.name }}
</li>
</ul>
<component :is="current.comName"></component>
</template>
<script setup lang='ts'>
import { ref, reactive,markRaw } from 'vue'
import ComponentsOne from '@/components/Dynamic/ComponentsOne.vue'
import ComponentsTwo from '@/components/Dynamic/ComponentsTwo.vue'
import ComponentsThree from '@/components/Dynamic/ComponentsThree.vue'
type tabs= {
name: string,
comName: any
}
let active=ref<number>(0)
//建议我们使用组件跳过代理 markRaw 使用的时候我们会打印出__v_skip:true;节省性能
const data =reactive(
[{name: 'A',comName: markRaw(ComponentsOne)},
{name: 'b',comName: markRaw(ComponentsTwo)},
{name: 'c',comName: markRaw(ComponentsThree)},
]
)
let current = reactive({
comName: data[0].comName
})
const changeComponent = (item:tabs,index:any)=>{
active = index
current.comName = item.comName
}
</script>
<style lang='scss' scoped>
.tab_list{
li{
cursor: pointer;
}
.active{
color: #f00;
}
}
</style>
知识点补充 markRaw 和 toRaw
可以使用v-slot: 或者简写为# 进行插槽
<template>
<son-slot>
<template v-slot:header>
<div>1</div>
</template>
<template v-slot>
<div>2</div>
</template>
<template #footer>
<div>3</div>
</template>
</son-slot>
</template>
<script setup lang='ts'>
import SonSlot from '@/components/slot/SonSlot.vue'
</script>
<template>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</template>
作用域插槽
<template>
<son-slot>
<template #defaults="{ data }">
<div>{{ data }}</div>
</template>
<template #[name]>
<div>我是动态插槽</div>
</template>
</son-slot>
</template>
<script setup lang='ts'>
import SonSlot from '@/components/slot/SonSlot.vue'
</script>
<template>
<div>
作用域插值
<div v-for="item in 20">
<slot :data="item" name="defaults"></slot>
</div>
</div>
</template>
动态插槽
<template>
<son-slot>
<template #[name]>
<div>我是动态插槽</div>
</template>
</son-slot>
</template>
<script setup lang='ts'>
import SonSlot from '@/components/slot/SonSlot.vue'
import { ref, reactive } from 'vue'
let name =ref('dongtai')
</script>
<template>
<slot name="dongtai"></slot>
</template>
路由跳转
router.ts
{
path: '/newsDetail/:id',
name: '/newsDetail',
component: () => import('@/view/NewsDetail.vue'),
},
router.push({
path: `/newsDetail/${item.news_id}`,
query: {
cateName: props.cateName
}
})
路由传参
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
console.log(route.query.cateName)
console.log(route.params.id)
开启keep-alive 生命周期的变化
初次进入时: onMounted> onActivated
退出后触发 deactivated
再次进入:
只会触发 onActivated
事件挂载的方法等,只执行一次的放在 onMounted中;组件每次进去执行的方法放在 onActivated中
使用Vue3的组合API封装的可复用的功能函数
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂.
mixins的缺点:
补充知识点
declare 是声明的关键字,通常用在编写声明文件,你可以通过 declare 去声明一个变量、声明一个函数等等,一旦声明了,TypeScript 编译器就知道它的存在,你就可以在任何地方使用了。
declare type AsideState = {
menuList: RouteRecordRaw[];
clientWidth: number;
};
// columnsAside
declare type ColumnsAsideState<T = any> = {
columnsAsideList: T[];
liIndex: number;
liOldIndex: null | number;
liHoverIndex: null | number;
liOldPath: null | string;
difference: number;
routeSplit: string[];
};
const state = reactive<AsideState>({
menuList: [],
clientWidth: 0,
});
<template>
<el-button type="primary" @click="testClick">test</el-button>
<teleport to='body' v-if="showTeleport">
<div class="m-bg">
<div class="m-title">
测试teleport
</div>
<div class="m-body">
<el-button @click="closeTeleport">关闭teleport</el-button>
</div>
</div>
</teleport>
</template>
<script lang="ts" setup>
const showTeleport = ref<boolean>(false)
const testClick = () => {
showTeleport.value = true;
}
const closeTeleport = () => {
showTeleport.value = false;
}
</script>
<style scoped lang="scss">
.m-bg{
width: 100%;
height: 100%;
background-color:rgba(5, 5, 5, 0.7);
position: absolute;
top: 0;
left: 0;
z-index: 99;
.m-title{
color: #fff;
}
}
</style>
1.安装echarts
npm install echarts --save
2.在页面引入
import * as echarts from "echarts";
3.初始化方法
(类型“HTMLElement | null”的参数不能赋给类型“HTMLElement”的参数。 不能将类)
var myChart = echarts.init(document.getElementById('allmetting') as HTMLElement);
另外:vite.config.ts的打包优化请移步:
因篇幅问题不能全部显示,请点此查看更多更全内容