Vite+Vue3+TypeScript基础知识案例_第1页
Vite+Vue3+TypeScript基础知识案例_第2页
Vite+Vue3+TypeScript基础知识案例_第3页
Vite+Vue3+TypeScript基础知识案例_第4页
Vite+Vue3+TypeScript基础知识案例_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Vite+Vue3+TypeScript2021年最新最完整Vite+Vue3+TypeScript基础知识案例<⼀>1、学习背景感觉实在学不动了,意料之外的是尤先⽣继续更新vue到3.0版本,以补充vue2.0的不⾜之处。随着vue3.0问世,vite2.5.1也油然⽽vite的到来,对于前端开发者⽽⾔,简直不要太幸福了。vue3.0不仅全⾯⽀持语⾔是前端未来发展的必然趋势,⽽⽣为vue家族的新成员vite也是前端技术爱好者的学习⽬标,赢在起点,从学习新技术开始。活到⽼,学到⽼,是⼀个合格前端开发者的毕⽣信仰。2、vite简介vite诞⽣是为了提升web项⽬运⾏性能,以更快的速度将应⽤页⾯展⽰给⽤户。Vite以⽅式提供源码。这实际上是让浏览器接管了打包程序的部分⼯作:Vite只需要在浏览器请求源码时进⾏转换并按需提供源码。根据情景动态导⼊代码,即只在当前屏幕上实际使⽤时才会被处理。提供的驱动⼒:、优化缓慢的服务器启动(冷启动开发服务器和正式环境响应速度);、优化缓慢的项⽬更新(vite服务器);[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-znQlBkN0-1642383363135)(E:\vue_project\javascript-demo\前端知识点\vite+vue3最新技术栈\vue3.1.png)][外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-Nro6Ko6e-1642383363136)(E:\vue_project\javascript-demo\前端知识点\vite+vue3最新技术栈\vue3.2.png)]3、vite创建项⽬兼容性注意:Vite需要版本>=12.0.0。vscode编辑器////安装vite1、npminitvite@latest//安装vite同时创建vite项⽬2、npminitvite@latestmy-vue-app--templatevue{"scripts":{"devvite"//启动开发服务器"buildvitebuild"//为⽣产环境构建产物"servevitepreview//本地预览⽣产构建产物}}4、版本依赖兼容和项⽬⽬录介绍package.json版本依赖说明,这⾥是整个项⽬依赖版本配置,相关安装指令后⾯视频中会逐个教⼤家如何安装。注意:vuex和router都是4.0及以上版本的,否则⽤vue3时,找不到暴露的api{{"name":"vite-ts-vue3-plus-demo","version":"0.0.0","scripts":{"dev":"vite","build":"vue-tsc--noEmit&&vitebuild","serve":"vitepreview"},"dependencies":{"@element-plus/icons":"0.0.11","dotenv":"^10.0.0","element-plus":"^1.1.0-beta.7","vue":"^3.0.5","vue-router":"^4.0.11","vuex":"^4.0.2"},"devDependencies":{"@types/node":"^16.7.1","@vitejs/plugin-vue":"^1.3.0","@vue/compiler-sfc":"^3.0.5","node-sass":"^6.0.1","sass":"^1.38.1","sass-loader":"^12.1.0","typescript":"^4.3.2","vite":"^2.4.4","vue-tsc":"^0.2.2"}}5、setup语法糖使⽤)注意:在setup()中不能⽤this`setup``this`,因为它不会找到组件实例。`setup``data`property、`computed`property`methods`被解析之前,所以`setup`中被获取,这也是为了避免setup()和其他选项式API混淆。/*/*参数说明props是响应式的,当传⼊新的prop时,它将被更新context是⼀个普通的上下⽂JavaScriptproperty(包括属性,插槽,⽅法),如下⽰例1所⽰*/⽰例1<script>exportdefault{setup(props,context){//Attribute⾮响应式对象)console.log(context.attrs)//插槽(⾮响应式对象)console.log(context.slots)//触发事件(⽅法)console.log(context.emit)}}</script>setup后世(⾼级⽤法),推荐⽤法<<scriptsetuplang="ts"><script>是在单⽂件组件(SFC)中使⽤组合式API的编译时的语法糖。相⽐普通的语法,它具有更多优势:更少的样板内容,更简洁的代码,⽐如:省略了组件的注册声明,对象暴露return,methods,。Typescriptprops和发出事件。更好的运⾏时性能(其模板会被编译成与其同⼀作⽤域的渲染函数,没有任何的中间代理)。更好的IDE类型推断性能(减少语⾔服务器从代码中抽离类型的⼯作)。注意点:1、在setup语法糖中导⼊组件不需要注册声明,直接在视图中使⽤即可;3、导⼊vue⽂件必须写上⽂件后缀名.vue,否则ts⽆法识别vue⽂件。⽰例对⽐:////基础⽤法<scriptlang="ts">exportdefault{props:{title:{type:String,default:()=>{return'测试信息'}}},setup(props:any){console.log(props.title)}}</script>//⾼级⽤法<scriptsetuplang="ts">constprops=defineProps({title:{type:String,default:()=>{return'测试信息'}}})console.log(props.title);</script>6、definePropsdefineEmits注意:definePropsdefineEmits<script注意:definePropsdefineEmits<scriptsetup>`props``emits``defineProps``defineEmits`API`<scriptsetup>`中都是⾃动可⽤的:**`defineProps``defineEmits``<scriptsetup>`中才能使⽤的****编译器宏**`<scriptsetup>`的时候被编译处理掉。`defineProps``props`选项相同的值,`defineEmits``emits`选项相同的值。`defineProps``defineEmits`在选项传⼊后,会提供恰当的类型推断。传⼊到`defineProps`和`defineEmits`的选项会从setup中提升到模块的范围。因此,传⼊的选项不能引⽤在setup范围中声明的局部变量。这样做会引起编译错误。但是,它*可以*引⽤导⼊的绑定,因为它们也在模块范围内。、⼦组件vue<template><template><p>{{props.msg}}</p><button@click="handleClick">点击我调⽤⽗组件⽅法</button></template><scriptsetuplang="ts">constprops=defineProps({msg:{type:String,default默认值'}})constemit=defineEmits(['on-change','update'])consthandleClick=()=>{emit('on-change','⽗组件⽅法被调⽤了')}</script>、⽗组件vue<script<scriptsetuplang="ts">importTestPropsPmitfrom'./components/test-props-emit/index.vue';import{ref}from'vue';//定义字符串变量constmsgref('欢迎使⽤vite!')//调⽤事件consthandleChange=(params:string)=>{console.log(params);}</script><template><TestPropsPmit:msg="msg"@on-change="handleChange"></TestPropsPmit></template>温馨提⽰:这⾥介绍⼀哈volar插件⼩图标在vue⽂件⾥的作⽤:点击这个三⾓形图标,会将⽂件归类显⽰,⽅便编写代码;[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-EbrgSaUR-1642383363137)(E:\vue_project\demos\vite-vue3-ts-volar\vite+vue3最新技术栈⽂档\volar.png)]7、正确使⽤defineExpose<script<scriptsetup>声明的绑定。<script<scriptsetup>

defineExpose组件中明确要暴露出去的属性,使⽤defineExpose

链获取到的组件的公开实例,不会暴露任何在 中$parent<script$parent<scriptsetup>、⼦组件暴露属性和⽅法,给⽗组件引⽤<scriptsetuplang="ts"><scriptsetuplang="ts">functiontestChild():void{console.log('⼦组件⽅法testChild被调⽤了');}constb=ref(2)//统⼀暴露属性defineExpose({obj:{name:'张三',age:2300},b,testChild})</script>、⽗组件调⽤⼦组件⽅法和属性<template><template><TestPropsEmitref="propsEmitRef":msg='msg'@on-change="handleChange"></template><scriptsetuplang="ts">importTestPropsEmitfrom'./components/test-props-emit/index.vue';import{ref,onMounted}from'vue';</TestPropsEmit>constmsgref('欢迎学习vite')consthandleChange=(params:string)=>{console.log(params);}constpropsEmitRef=ref()onMounted(()=>{console.log(propsEmitRef.value.child);})</script>在setup如何定义变量(字符串,对象,数组)<template><template><h2>{{count}}{{}}</h2><spanv-for="(item,index)inarr":key="index">{{item}}</span><button@click="setName">点击我增加</button></template><scriptsetuplang="ts">import{ref,reactive}from'vue';//字符串变量constcount=ref(0)//对象letuser=reactive({name:'张三'})//数组letarr=reactive(['1','2','3'])//综合定义⽅案constoriginData=reactive({count:0,user:{name:'张三'},arr:['1','2','3']})//⽅法setName=()=>{count.value++李四'}</script>8、Watch和WatchEffect1、基本使⽤⽅法:<template><p>{{originData.count}}{{originD}}</p><pv-for="(item,index)inoriginData.arr":key="index">{{item}}</p><button@click="incriment">点击我count增加</button></template><scriptsetuplang="ts">import{ref,reactive,watchEffect,watch}from'vue';constcount=ref(0)constuser=reactive({name:'张三'})constarrreactive([1,2,3,4])//综合定义⽅案constoriginData=reactive({count:0,user:{name:'张三'},arr:[1,2,3,4]})constincriment=()=>{originData.count++count.value++originD='李四'}//默认页⾯更新之前⽴即执⾏监听,懒执⾏开始watchEffect(()=>console.log(count.value))//默认监听数据变化后的值,页⾯更新后不会⽴即执⾏watch(count,(n,o)=>{console.log('watch',n,o);})//监听多个值watch([count,originData.user],(newValues,prevValues)=>{console.log(newValues[0],newValues[1].name)})//⽴即监听watch([count,originData.user],(newValues,prevValues)=>{console.log(newValues[0],newValues[1].name)},{deep:true,immediate:true})</script>2、watch与watchEffect⽐较,推荐watch监听watchwatchEffect如果要实现:watch在页⾯更新之后就执⾏,需要增加⽴即执⾏的属性;watch([count,originData.user],watch([count,originData.user],(n,o)=>{console.log(n[0],n[1].name)},{deep:true,immediate:true})都懒执⾏副作⽤,不过watchEffect⽐较频繁,在vue组件更新之前执⾏;2、watch更具体地说明什么状态应该触发侦听器重新运⾏,watchEffect就没有这么友好;3、watch访问侦听状态变化前后的值。9、在setup中的⽣命周期钩⼦setupcreated因为 是围绕beforeCreate和 ⽣命周期钩⼦运⾏的,所以不需要显式地定义它们。换句话说,在这些钩⼦中编写的任何代码setupcreated都应该直接在函数中编写。下表包含如何在内部调⽤⽣命周期钩⼦:选项式APIHookinsidesetupbeforeCreateNotneeded*createdNotneeded*beforeMountonBeforeMount挂载之前mountedonMounted页⾯加载完成时执⾏beforeUpdateonBeforeUpdateupdatedonUpdatedbeforeUnmountonBeforeUnmountunmountedonUnmounted页⾯销毁时执⾏errorCapturedonErrorCapturedrenderTrackedonRenderTrackedrenderTriggeredonRenderTriggeredactivatedonActivateddeactivatedonDeactivated<scriptsetuplang="ts">import{onMounted,onActivated,onUnmounted,onUpdated,<scriptsetuplang="ts">import{onMounted,onActivated,onUnmounted,onUpdated,onDeactivated}from'vue';//读取环境变量constmode=import.meta.env;//importHeadMenufrom'@/components/head-menu/index.vue';onMounted((){console.log("组件挂载")})onUnmounted(()=>{console.log("组件卸载")})onUpdated((){console.log("组件更新")})onActivated((){console.log("keepAlive组件激活")})onDeactivated((){console.log("keepAlive组件⾮激活")})</script>10、⽤Ts限制define(Emits|Props)参数类型注意:1、在setup语法糖中引⼊组件不需要注册声明就可以直接⽤了24、⽤ts⽅式限制defineEmits和defineProps参数类型1、⼦组件<template><h1>{{msg}}</h1><h3>{{title}}</h3><inputv-model="inputValue"type="text"@blur="handleUpdate($event)"></template><scriptsetuplang="ts">import{ref}from"vue";defineProps<{msg?:(string|number),title?:string}>()//提供默认值⽅式1interfaceProps{msg?:(string|number|boolean),title?:string[]}withDefaults(defineProps<Props>(),{msg:'hello',title:()=>['one','two']})//提供默认⽅式2withDefaults(defineProps<{msg?:(string|number|boolean)title?:string}>(),{msg:3,title默认标题'})//constemit=defineEmits(['updateValue'])constemit=defineEmits<{(event:'change'):void,(event:'update',data:string):void}>()constinputValue=ref<any>()consthandleUpdate=(event:any)=>{const{target}=event//console.log(target.value,1111);emit('update',event.target.value)}</script>2、⽗组件<script<scriptsetuplang="ts">importCellSamplefrom"./components/cell-samples/index.vue";constupdate=(data:any)=>{console.log(data);}</script><template><CellSample@update="update"></CellSample></template>11、递归组件⼀个单⽂件组件可以通过它的⽂件名被其⾃⼰所引⽤。例如:名为

的组件可以在其模板中⽤

引⽤它⾃⼰。FooBar.vue<FooBar/>请注意这种⽅式相⽐于import导⼊的组件优先级更低。如果有命名的import导⼊和组件的推断名冲突了,可以使⽤import别名导⼊:FooBar.vue<FooBar/>importimport{FooBarasFooBarChild}from'./components'注意:这⾥有⼩问题,当单⽂件引⼊单⽂件时会出现内存溢出现象<scriptsetup>:is12、<scriptsetup>:is由于组件被引⽤为变量⽽不是作为字符串键来注册的,在

中要使⽤动态组件的时候,就应该使⽤动态的

来绑定:<scriptsetuplang='ts'><scriptsetuplang='ts'>importFoofrom'./Foo.vue'importBarfrom'./Bar.vue'</script><template><component:is="Foo"/><component:is="someCondition?Foo:Bar"/></template>13、ts限制普通函数/箭头函数参数类型普通函数<script<scriptsetuplang="ts">functiontest(params:(string|boolean)):void{console.log(params);}test('5555')</script>箭头函数,推荐⽤法<script<scriptsetuplang="ts">consttest=(params:(string|boolean))=>{console.log(params)}test('5555')</script>14、引⼊vuex配置和使⽤创建项⽬时我们已经引⼊了vuex4.0版本,接下来我们就开始配置vuex4.0并测试。////注意必须安装vuex4.0版本及以上npminstallvuex@next--save#oryarnaddvuex@next--save在src⽬录下创建store⽂件夹,新建⽂件index.tsindex.ts内容如下所⽰:import{InjectionKey}from'vue'/**引⼊InjectionKey并将其传⼊useStore使⽤过的任何地⽅,很快就会成为⼀项重复性的⼯作。为了简化问题,可以定义⾃⼰的组合式函数来检索类型化的store*///未简化useStore版//import{createStore,Store}from'vuex'//简化useStore版import{useStoreasbaseUseStore,createStore,Store}from'vuex'//storestate声明类型exportinterfaceState{username:string,count:number}//injectionkeyexportconstkey:InjectionKey<Store<State>>=Symbol()//导出store模块exportconststore=createStore<State>({state:{username:"测试store",count:0},getters:{getName:state=>{returnstate.username}},mutations:{//重置名称SET_NAME(state,params:string){state.username=params}},actions:{}})//`useStore`组合式函数exportfunctionuseStore(){returnbaseUseStore(key)}在根⽬录下新建vuex-d.ts⽂件,内容如下所⽰:////⼀个声明⽂件来声明Vue的⾃定义类型ComponentCustomPropertiesimport{ComponentCustomProperties}from'vue';import{Store}from'vuex';declaremodule'@vue/runtime-core'{//storestateinterfaceState{count:number,username:string}//`this.$store`提供类型声明interfaceComponentCustomProperties{$store:Store<State>}}在main.ts中注⼊store模块importimport{createApp}from'vue';importAppfrom'./App.vue';//导⼊store模块,传⼊injectionkeyimport{store,key}from'@/store';constapp=createApp(App)app.use(store,key)app.mount('#app')引⽤测试vuex配置是否正确<el-button<el-button@click="changeName"size="small">点击修改名称</el-button><scriptsetuplang="ts">//vue组件import{useStore}from'@/store';conststore=useStore()//测试store重置名称//mit('increment',10)functionchangeName():void{mit('SET_NAME',10)console.log('修改后的名称:'+store.getters.getName);}console.log(store.state.count,store.getters.getName)</script>15、router配置以及使⽤详解安装router4.0并测试。////注意:安装router必须是4.0及以上npminstallvue-router@4页⾯准备⽬录结构[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-O1byer9d-1642383363137)(E:\vue_project\demos\vite-vue3-ts-volar\vite+vue3最新技术栈⽂档\page.png)]页⾯具体内容:1、layout/index.vue<template><template><Header/><router-view></router-view></template><scriptsetuplang="ts">importHeaderfrom'./header/index.vue';</script>2、layout/header/index.vue<template><template><divclass="action"><h2@click="handleClick(1)">⾸页</h2><h2@click="handleClick(0)">关于</h2></div></template><scriptsetuplang="ts">import{useRouter}from'vue-router';constrouter=useRouter()consthandleClick=(num:number)=>{if(num){router.push({name:'home'})}elserouter.push({name:'about'})}</script>.action{display:flex;}h2{padding:0px10px;cursor:pointer;}h2:hover{}</style>3、pages/home/index.vue<template><template><h2>home</h2></template>4、pages/about/index.vue<template><template><h2>about</h2></template>在src⽬录下创建router⽂件夹,然后创建index.ts⽂件,内容如下所⽰:importimport{createRouter,createWebHashHistory}from"vue-router";importLayOutfrom"../components/layout/index.vue";constroutes=[{path:'/',component:LayOut,redirect:'/home',children:[{path:'/home',name:'home',component:()=>import("../pages/home/index.vue"),meta:{title⾸页',icon:''}},{path:'/about',name:'about',component:()=>import("../pages/about/index.vue"),meta:{title关于',icon:''}}]}]constrouter=createRouter({history:createWebHashHistory(),routes})exportdefaultrouter中注⼊routerimportimport{createApp}from'vue'importAppfrom'./App.vue'import{store,key}from'./store';importrouterfrom'./router';constapp=createApp(App);app.use(store,key);app.use(router);app.mount('#app');使⽤路由<template><template><divclass="action"><h2@click="handleClick(1)">⾸页</h2><h2@click="handleClick(0)">关于</h2></div></template><scriptsetuplang="ts">import{useRouter}from'vue-router';constrouter=useRouter()consthandleClick=(num:number)=>{if(num){router.push({name:'home'})}elserouter.push({name:'about'})}</script>.action{display:flex;}h2{padding:0px10px;cursor:pointer;}h2:hover{}</style>16、引⼊element-plus以及注意事项安装npmnpminstallelement-plus--save#oryarnaddelement-plus#安装icon图标依赖库npminstall@element-plus/icons#oryarnadd@element-plus/icons在main.tsimportimport{createApp}from'vue'importAppfrom'./App.vue'import{store,key}from'./store';//注⼊路由importrouterfrom'./router';//引⼊ui组件importElementPlusfrom'element-plus'import'element-plus/dist/index.css'constapp=createApp(App);app.use(store,key);app.use(router);app.use(ElementPlus);app.mount('#app');在vue⽂件中引⽤ui组件1、单个图标引⽤<template><template><el-icon:size="20":color="'blue'"><edit/></el-icon></template><scriptsetuplang="ts">import{Edit}from'@element-plus/icons'</script>2、全局注册图标使⽤importimport{createApp}from'vue'importAppfrom'./App.vue'import{Edit,Search}from'@element-plus/icons'constapp=createApp(App);ponent("edit",Edit)ponent("search",Search)app.mount('#app');2、1使⽤图标<template><template><h2>home页⾯</h2><el-buttontype="primary">主要按钮</el-button><el-buttontype="success">成功按钮</el-button><el-icon:size="20":color="'blue'"><edit/></el-icon><el-icon:size="20"><search></search></el-icon></template><scriptsetuplang="ts"></script>17、配置vite.config.ts这⾥主要配置vite服务器的打包保存地址,打包分解,端⼝号,是否⾃动打开浏览器,远程请求地址代理⽬标,⽬录别名,全局样式配置等。import{defineConfig}from'vite';importvuefrom'@vitejs/plugin-vue';import{loadEnv}from'vite';//nodejs写法,获取项⽬⽬录importpathfrom'path';//https://vitejs.dev/config/exportdefault({command,mode})=>{returndefineConfig({plugins:[vue()],server:{host:'',port:Number(loadEnv(mode,process.cwd()).VITE_APP_PORT),strictPort:true,//端⼝被占⽤直接退出https:false,open:true,//在开发服务器启动时⾃动在浏览器中打开应⽤程序proxy:{//字符串简写写法//'/foo':'',//选项写法'/api':{target:loadEnv(mode,process.cwd()).VITE_APP_BASE_URL,changeOrigin:true,rewrite:(path)=>path.replace(/^\/api/,'')},//正则表达式写法//'^/fallback/.*':{//target:'',//changeOrigin:true,//rewrite:(path)=>path.replace(/^\/fallback/,'')//},},hmr:{//屏蔽服务器报错}},resolve:{alias:{'@':path.resolve(__dirname,'./src')}},css:{//css预处理器preprocessorOptions:{//引⼊var.scss这样就可以在全局中使⽤var.scss中预定义的变量了;scss:{additionalData:'@import"@/assets/styles/global.scss";'}}},build:{chunkSizeWarningLimit:1500,//分块打包,分解块,将⼤块分解成更⼩的块rollupOptions:{output:{manualChunks(id){if(id.includes('node_modules')){returnid.toString().split('node_modules/')[1].split('/')[0].toString();}}}}}})}18、tsconfig.json配置{{"compilerOptions":{"target":"esnext","module":"esnext","moduleResolution":"node","strict":true,"jsx":"preserve","sourceMap":true,"resolveJsonModule":true,"esModuleInterop":true,"lib":["esnext","dom"],"baseUrl":".","paths":{"@/*":["src/*"]}},"include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]}19、shims-vue.d.ts配置声明vue⽂件全局模块declaredeclaremodule'*.vue'{import{DefineComponent}from'vue'//eslint-disable-next-line@typescript-eslint/no-explicit-any,@typescript-eslint/ban-typesconstcomponent:DefineComponent<{},{},any>exportdefaultcomponent}//注意要使⽤scss,必须安装依赖,否则报错//注意要使⽤scss,必须安装依赖,否则报错npminstallnode-sasssass-loadersass-D需要在src⽬录的assets静态⽬录新增⼀个全局global.scss⽂件,其他样式⽂件导⼊到该⽂件即可全局使⽤和修改。[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-OMnks85U-1642383363138)(E:\vue_project\demos\vite-vue3-ts-volar\vite+vue3最新技术栈⽂档\csss.png)]写⼀个单独属性测试$color-primary:$color-primary:#007aff;vue使⽤全局样式变量<template><h2<template><h2class="test-color">home页⾯</h2></template><stylelang="scss"scoped>.test-color{color:$color-primary;}</style>在vite.config.ts⽂件中配置global.scsscsscss:{//css预处理器preprocessorOptions:{//引⼊var.scss这样就可以在全局中使⽤var.scss中预定义的变量了scss:{additionalData:'@import"@/assets/styles/global.scss";'}}}21、响应式API、refsetup()要明确使⽤来创建。和从 函数中返回值⼀样,ref值在模板中使⽤的时候会⾃动解包:setup()<scriptsetuplang='ts'><scriptsetuplang='ts'>import{ref}from'vue'constcount=ref(0)</script><template><button@click="count++">{{count}}</button></template>、toRefs当从组合式函数返回响应式对象时,toRefs⾮常有⽤,这样消费组件就可以在不丢失响应性的情况下对返回的对象进⾏解构/展开:functionfunctionuseFeatureX(){conststate=reactive({foo:1,bar:2})//state的逻辑//返回时转换为refreturntoRefs(state)}exportdefault{setup(){//可以在不失去响应性的情况下解构const{foo,bar}=useFeatureX()return{foo,bar}}}toRefspropertyrefpropertyref,则应当使⽤21.3、roReftoRefsconstconststate=reactive({foo:1,bar:2})constfooRef=toRef(state,'foo')fooRef.value++console.log(state.foo)//2state.foo++console.log(fooRef.value)//3即使源property不存在,toRef也会返回⼀个可⽤的ref。这使得它在使⽤可选prop时特别有⽤,可选prop并不会被处理。22、.env.环境变量配置和读取.duction//⽣产环境配置⽂件.env.development//开发环境配置⽂件⽣产和开发环境配置⽂件内容如下:##开发环境/#⽣产环境VITE_APP_TITLE前端技术栈"VITE_APP_PORT=3001请求接⼝VITE_APP_BASE_URL="2:8080"//env.d.ts⽂件内进⾏环境变量智能提⽰配置interfaceImportMetaEnv{VITE_APP_TITLE:string,VITE_APP_PORT:string,VITE_APP_BASE_URL:string}vite.config.ts读取配置⽂件importimport{defineConfig}from'vite';importvuefrom'@vitejs/plugin-vue';import{loadEnv}from'vite';//nodejs写法,获取项⽬⽬录importpathfrom'path';//https://vitejs.dev/config/exportdefault({command,mode})=>{//console.log(command,mode,5555);returndefineConfig({plugins:[vue()],server:{host:'',port:Number(loadEnv(mode,process.cwd()).VITE_APP_PORT),strictPort:true,//端⼝被占⽤直接退出https:false,open:true,//在开发服务器启动时⾃动在浏览器中打开应⽤程序proxy:{//字符串简写写法//'/foo':'',//选项写法'/api':{target:loadEnv(mode,process.cwd()).VITE_APP_BASE_URL,changeOrigin:true,rewrite:(path)=>path.replace(/^\/api/,'')},//正则表达式写法//'^/fallback/.*':{//target:'',//changeOrigin:true,//rewrite:(path)=>path.replace(/^\/fallback/,'')//},//proxy实例//'/api':{//target:'',//changeOrigin:true,//configure:(proxy,options)=>{// //proxy是'http-proxy'的实例//},//}},hmr:{//屏蔽服务器报错}},resolve:{alias:{'@':path.resolve(__dirname,'./src')}},css:{//css预处理器preprocessorOptions:{//引⼊var.scss这样就可以在全局中使⽤var.scss中预定义的变量了;scss:{additionalData:'@import"@/assets/styles/global.scss";'},sass:{additionalData:'@import"@/assets/styles/global.scss";'}}},build:{chunkSizeWarningLimit:1500,//分块打包,分解块,将⼤块分解成更⼩的块rollupOptions:{output:{manualChunks(id){if(id.includes('node_modules')){returnid.toString().split('node_modules/')[1].split('/')[0].toString();}}}}}})}其他⽂件读取环境变量<template><template>⾸页内容展⽰</template><scriptsetuplang="ts">import{onMounted}from'vue';//读取环境变量constmode=import.meta.env;onMounted(()=>{console.log(mode,555);})</script>23、【补充】mixin混⼊模式mixinmixin对象的选项将被“混合”进⼊该组件本⾝的选项。Mixin缺点温馨提⽰:在Vue2中,mixin是将部分组件逻辑抽象成可重⽤块的主要⼯具。但是,他们有⼏个问题:他每个特性。可重⽤性是有限的:我们不能向mixin传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑⽅⾯的灵活性。为了解决这些问题,我们添加了⼀种通过逻辑关注点组织代码的新⽅法:。换⾔之:在vue3.0⾥是不推荐使⽤mixin混⼊开发的,更加推荐使⽤组合式API,将页⾯操作数据的功能进⾏代码拆分,更好的发挥⼤型项⽬下共享和复⽤代码,在代码的可维护性和扩展性得以长存。<scriptsetup><script<scriptsetup><scriptsetup>

的时候,任何在

声明的顶层的绑定(包括变量,函数声明,以及import引⼊的内容)都能在模板中直<script<scriptsetuplang="ts">import{capitalize}from'./helpers'//变量constmsg='Hello!'//函数functionlog(){console.log(msg)}</script><template><div@click="log">{{msg}}</div><div>{{capitalize('hello')}}</div></template>25、【补充】teleport传送门<teleport><teleport>在其中移动 内容的⽬标元<teleport><teleport>素,disabled-boolean此可选属性可⽤于禁⽤指定了<teleport>的位置渲染。创建带传送门的组件

的功能,这意味着其插槽内容将不会移动到任何位置,⽽是在你在周围⽗组件中<template><template><teleportto=".teleport-one"><div>我是Teleport内容</div></teleport></template>teleport混合component使⽤<template><template><divclass="teleport-one">传送门1</div><divclass="teleport-two">传送门2</div><TestTeleport></TestTeleport></template><scriptsetuplang="ts">importTestTeleportfrom"@/components/test-teleport/index.vue";</script>26、【补充】computed使⽤refwatchcomputed与 和 类似,也可以使⽤从Vue导⼊的 函数在Vue组件外部创建计算属性。让我们回到counter的例⼦:refwatchcomputedimportimport{ref,computed}from'vue'constcounter=ref(0)consttwiceTheCounter=computed(()=>counter.value*2)counter.value++console.log(counter.value)//1console.log(twiceTheCounter.value)//2注意:////不能这样使⽤twiceTheCounter.value++//错误⽤法Vite+Vue3+Setup+TypeScript布⾐博客项⽬实战<⼆>序⾔:⾸先感谢各位前端朋友的⽀持和⿎励,你们的⿎励和理解是我前进路上的动⼒源泉。从本项⽬学到更多的是开发过程中的细节和注意事项。活信息化研发需求,进⼀步从细节剖析各个技术知识点,布⾐前端特此出品名为:《布⾐博客》的web⽹页设计。⽬的是为了更好掌握Vite+Vue3+setup+TypeScript最新的web技术栈,熟练使⽤它们进⾏研发出属于⾃⼰的或者企业级的应⽤项⽬。本博客专门针对setup+ts进⾏开发,省略传统vue2的api。没有vue2基础知识的朋友,建议看完vue3再来学习效果更佳。温馨提⽰:布⾐博客项⽬是依赖于基础知识案例部分进⾏改造的,项⽬的创建和各个依赖的安装,不在赘述和重演,所以没有把基础知识学完的朋友,最好学完后在来浏览项⽬实战部分。获取基础知识项⽬的渠道:1、项⽬依赖升级既然项⽬要玩最新的,依赖当然得更新到最新版旧版本依赖:"dependencies":"dependencies":{"@element-plus/icons":"0.0.11","element-plus":"^1.1.0-beta.12","vue":"^3.2.11","vue-router":"^4.0.11","vuex":"^4.0.2"},"devDependencies":{"@types/node":"^16.9.2","@vitejs/plugin-vue":"^1.6.0","@vue/compiler-sfc":"^3.2.6","node-sass":"^6.0.1","sass":"^1.41.1","sass-loader":"^12.1.0","typescript":"^4.3.2","vite":"^2.5.2","vue-tsc":"^0.2.2"}新版本依赖:"dependencies":"dependencies":{"@element-plus/icons":"0.0.11","element-plus":"^1.2.0-beta.2",//原版本1.1.0-beta.12"vue^3.2.21"//原版本3.2.11"vue-router":"^4.0.12","vuex":"^4.0.2"},"devDependencies":{"@types/node":"^16.9.2","@vitejs/plugin-vue":"^1.6.0","@vue/compiler-sfc":"^3.2.6","node-sass":"^6.0.1","sass":"^1.41.1","sass-loader":"^12.1.0","typescript":"^4.3.2","vite":"^2.6.13",//原版本2.5.2"vue-tsc":"^0.28.10"//原版本0.2.2}2、创建顶部菜单组件menu⽂件夹,然后新建menu-bar.vue,完整内容如下。<template><el-menu:default-active="activeIndex2"class="el-menu-demo"mode="horizontal"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"@select="handleSelect"><templatev-for="(item,index)inmenuList":key="item.path"><templatev-if="!item.children"><el-menu-item:index="item.meta?.index"@click="handleRoute(item)">{{item.meta?.title}}</el-menu-item></template><templatev-else><el-sub-menu:index="item.meta?.index":popper-append-to-body="false"><template#title>{{item.meta?.title}}</template><el-menu-item:index="childItem.meta?.index"v-for="(childItem,subIndex)initem.children":key="childItem.path"@click="handleRoute(childItem)">{{childItem.meta?.title}}</el-menu-item></el-sub-menu></template></template></el-menu></template><scriptlang="ts"setup>import{ref,computed}from'vue';import{useRoute,useRouter}from"vue-router";import{mapState}from'vuex';import{useStore}from'@/store';conststore=useStore()constrouter=useRouter()//定义变量constactiveIndex2=computed(mapState(['currentMenu']).currentMenu.bind({$store:store}))||ref<string>('1')//获取菜单路由constmenuList=router.options.routes[0].children;//定义事件//点击菜单事件consthandleSelect=(key:string,keyPath:string)=>{console.log(key,keyPath)mit('SET_CURRENT_MENU',key)}//跳转关于页⾯consthandleRoute=(route:any)=>{router.push(route.path)}</script><stylelang="scss">@import"./style.scss";</style>在同级menu⽂件夹下新建style.scss,内容如下:在header.vue引⼊menu-bar.vue<template><template><menu-bar></menu-bar></template><scriptsetuplang="ts">importMenuBarfrom'@/components/layout/menu/menu-bar.vue';</script><stylelang="scss"scoped></style>在assets⽬录⾥的styles⽬录下新增body.scss⽂件,内容如下body{body{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;width:100%;margin:0;padding:0;background-color:#ececec;}然后在layout.scss内部导⼊@import@import"./body.scss";3、修复选中菜单刷新丢失问题import{InjectionKey}from'vue';import{useStoreasbaseUseStore,createStore,Store}from'vuex';interfaceState{username:string,currentMenustring//当前菜单}exportconstkey:InjectionKey<Store<State>>=Symbol();exportconststore=createStore<State>({state:{username张三',currentMenu:localStorage.getItem('currentMenu')||'1'},getters:{getName:(state:State)=>{returnstate.username},getCurrentMenu:(state:State)=>{returnstate.currentMenu||localStorage.getItem('currentMenu')}},mutations:{SET_USERNAME(state:State,username:string){state.username=username;},SET_CURRENT_MENU(state:State,params:string){localStorage.setItem('currentMenu',params)state.currentMenu=params}},actions:{}})exportfunctionuseStore(){returnbaseUseStore(key)}{{"compilerOptions":{"target":"esnext",//⽬标语⾔的版本"moduleesnext"//指定⽣成代码的模板标准"moduleResolution":"node",//node模块解析"stricttrue//启⽤严格模式"jsxpreserve"//jsx模板解析"noImplicitAny":true//any类型"removeCommentstrue//删除注释"sourceMap":true,//⽣成⽬标⽂件的sourceMap⽂件"strictNullCheckstrue,//不允许把null、undefined赋值给其他类型的变量"resolveJsonModuletrue//允许导⼊.json⽂件"esModuleInteroptrue,//允许导⼊额外的ts⽂件⽀持"suppressImplicitAnyIndexErrorstrue//允许字符串下标表达式"lib":["esnext","dom"],"baseUrl":".",//解析⾮相对模块的基地址,默认是当前⽬录,防⽌引⼊⽂件报错"paths":{//路径映射,相对于baseUrl"@/*":["src/*"]},"skipLibCheck":true//所有的声明⽂件(*.d.ts)的类型检查,解决:打包不报错},//指定⼀个匹配列表(属于⾃动指定该路径下的所有ts相关⽂件)"src/**/*.vue"]}4、router.ts的RouteRecordRaw类型校验为了规范化typescript开发,增加路由对象类型限制,好处:允许在基础路由⾥⾯增加开发者⾃定义属性。import{createRouter,createWebHashHistory,RouteRecordRaw}from'vue-router';importLayOutfrom'@/components/layout/index.vue';constroutes:Array<RouteRecordRaw>=[{path:"/",component:LayOut,redirect:"/home",hidden:true,children:[{path:"/home",name:"home",hidden:false,meta:{title⾸页',index:'1'},component:()=>import("@/pages/home/index.vue")},{path:"/about",name:"about",meta:{title关于',index:'2'},component:()=>import("@/pages/about/index.vue")}]}]constrouter=createRouter({//指定路由模式history:createWebHashHistory(),//路由地址routes})exportdefaultrouter;注意:在给路由对象新增类型后,在每个路由对象⾥新增hidden或者其他属性会报错,解决办法:在src⽬录下新增⼀个路由声明⽂件,扩展基础路由对象属性。vue-router.d.ts,内容如下:importimport{_RouteRecordBase}from'vue-router';declaremodule'vue-router'{interface_RouteRecordBase{hidden?:boolean|string|number}}5、通过beforeEach修改浏览器标题⽅法⾥设置即可。routerrouter.beforeEach((to,from,next)=>{//设置浏览器标题if(to.meta.title){document.title=String(to.meta.title)}else{document.title="布⾐博客"}//console.log(to,from,router);next()})6、路由导航出错处理在实际项⽬开发中,经常遇到路由导航报错或者找不到问题,那么处理这个问题就变得很有必要。解决⽅法就是在main.ts⽂件中的router.beforeEach⽅法⾥判断即可。importimport{createApp}from'vue'importAppfrom'./App.vue'import{store,key}from'./store';//注

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论