vue新版本解析,代号”one-piece“
其他组件 clipboard-polyfill xgplayer qrcode
vue-echarts 安装
1 npm install echarts vue-echarts
在vue2中使用还需要安装compostion包
1 npm i -D @vue/composition-api
如果在NuxtJS中使用还需要安装另外一个包
1 npm i -D @nuxtjs/composition-api
并且在nuxt.config.js中添加配置
1 @nuxtjs/composition-api/module
在vue3中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import { createApp } from 'vue' import ECharts from 'vue-echarts' import { use } from "echarts/core" import { CanvasRenderer } from 'echarts/renderers' import { BarChart } from 'echarts/charts' import { GridComponent, TooltipComponent } from 'echarts/components' use([ CanvasRenderer, BarChart, GridComponent, TooltipComponent ]) const app = createApp(...)app.component('v-chart' , ECharts) app.mount(...)
在vue2中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import Vue from 'vue' import ECharts from 'vue-echarts' import { use } from 'echarts/core' import { CanvasRenderer } from 'echarts/renderers' import { BarChart } from 'echarts/charts' import { GridComponent, TooltipComponent } from 'echarts/components' use([ CanvasRenderer, BarChart, GridComponent, TooltipComponent ]); Vue.component('v-chart' , ECharts) new Vue(...)
轮播图 安装包
1 npm install vue-awesome-swiper --save
引入
1 2 3 4 5 6 7 8 9 import VueAwesomeSwiper from 'vue-awesome-swiper' import 'swiper/dist/css/swiper.css' Vue.use(VueAwesomeSwiper) import 'swiper/dist/css/swiper.css' import {Swiper,swiperSlide} from 'vue-awesome-swiper'
修改swiper属性,自动播放与延时
验证码 https://www.npmjs.com/package/vue2-verify
多级菜单 递归组件实现
子组件,MenuItem 是一个 li 标签和 slot 插槽,允许往里头加入各种元素
1 2 3 4 5 6 // Menuitem <template> <li class="item"> <slot /> </li> </template>
父组件Menu,Menu中有两种情况需要做判断,一种是 item 没有 children 属性,直接在 MenuItem 的插槽加入一个 span 元素渲染 item 的 title 即可;另一种是包含了 children 属性的 item 这种情况下,不仅需要渲染 title 还需要再次引入 Menu 做递归操作,将 item.children 作为路由传入到 router prop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <!-- Menu --> <template> <ul class="wrapper"> <!-- 遍历 router 菜单数据 --> <menuitem :key="index" v-for="(item, index) in router"> <!-- 对于没有 children 子菜单的 item --> <span class="item-title" v-if="!item.children">{{item.name}}</span> <!-- 对于有 children 子菜单的 item --> <template v-else> <span @click="handleToggleShow">{{item.name}}</span> <!-- 递归操作 --> <menu :router="item.children" v-if="toggleShow"></menu> </template> </menuitem> </ul> </template> <script> import MenuItem from "./MenuItem"; export default { name: "Menu", props: ["router"], // Menu 组件接受一个 router 作为菜单数据 components: { MenuItem }, data() { return { toggleShow: false // toggle 状态 }; }, methods: { handleToggleShow() { // 处理 toggle 状态的是否展开子菜单 handler this.toggleShow = !this.toggleShow; } } }; </script>
路由数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [ { name: "About" , path: "/about" , children: [ { name: "About US" , path: "/about/us" }, { name: "About Comp" , path: "/about/company" , children: [ { name: "About Comp A" , path: "/about/company/A" , children: [ { name: "About Comp A 1" , path: "/about/company/A/1" } ] } ] } ] }, { name: "Link" , path: "/link" } ];
markdown编辑器 mavon-editor
1 npm install mavon-editor --save
在app.js引入
1 2 import mavonEditor from 'mavon-editor' Vue.use(mavonEditor)
在页面引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <template> <div> <mavon-editor v-model='content'/> </div> </template> <script> export default{ data(){ return{ content:'', } }, } </script>
直播 播放器使用的是 vue-video-player ,其实就是 video.js 集成到 vue 中,后台主要输出 RTMP 和 HLS 的直播流
如果需要播放 HLS 流,需要安装 videojs-contrib-hls 插件,非原生支持的浏览器,直播服务端需要开启 CORS(后面会讲到)
如果需要播放 RTMP 流,需要安装 videojs-flash 插件
如果两个流都需要播放,flash 插件需要安装到 hls 插件之前
兼容性:
RTMP: 上面说了 RTMP 是 Adobe 公司研发的协议,目前主要的直播服务都主推 RTMP 流,它延时小,但是需要 flash 插件的支持,也需要的上面提到的安装 videojs-flash
的插件。但是在 MAC 下对 flash 插件支持不友好,而且 MAC 下的 flash 插件 firefox 浏览器和 chrome 还是两个插件。。这就很尴尬。
HLS: 这个协议兼容性较好,但是最大的缺点是延迟较高,大概 20s 左右,所以只能当做备选方案。
说 HLS 兼容性较好,主要是指可以通过 JS 让用户免配置(不必安装flash),可以在 caniuse 看下 HLS 的支持程度
最后我们使用的方案是。优先使用 RTMP 流,如果不支持,就切换到 HLS 流。好在这个切换过程 video.js 会自动替我们做。下面贴一下相关配置代码。
https://segmentfault.com/a/1190000011346597
循环播放插件 Vue-seamless-scroll
安装
1 npm install vue-seamless-scroll --save
在文件中引入
1 2 3 4 5 6 7 8 9 10 11 import vueSeamlessScroll from 'vue-seamless-scroll' export default { data(){ return { components:{ vueSeamlessScroll } } } }
在页面中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template> <vue-seamless-scroll :data="Array" :class-option="classOption"> <ul> <li> </li> </ul> </vue-seamless-scroll> </template> <script> export default { computed:{ classOption () { return { //上下滚动时父容器指定height和overflow:hidden,左右滚动时指定width step:0.2 //数值越大,滚动速度越快 limitMoveNum: 2 //开始无缝滚动的数据量 hoverStop: true //是否开启鼠标悬停stop direction: 0 //0向下,1向上,2向左,3向右 } } } } </script>
vue-auth 安装包
1 npm install @websnaova/vue-auth
vue-pdf 输出导入excel文件 使用sheetjs库
安装
输出打印 vue-print-nb
安装
1 npm install --save vue-print-nb
在main.js中全局引入
1 2 3 import Print from 'vue-print-nb' Vue.use(Print)
通过id打印
1 2 3 4 5 6 7 8 <tr id="printInfo"> </tr> <el-button type = "primary" key = "printInfo" v-print = "'#printInfo'"> </el-button>
要打印的组件必须渲染出来,否则会报错。
地图 leafletjs
leaflet是一个对移动端优化的交互地图并且开源的JavaScript库,是一个十分轻量级的WebGIS库。
安装
1 npm install leaflet --save
引入
vue-amap
安装包
1 npm install -S vue-amap // 可在package.json查看是否安装
在入口文件main.js引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import VueAMap from 'vue-amap' Vue.config.productionTip = false Vue.use(VueAMap); VueAMap.initAMapApiLoader({ key: 'your amap key' , plugin: [ 'AMap.Autocomplete' , 'AMap.PlaceSearch' , 'AMap.Scale' , 'AMap.OverView' , 'AMap.ToolBar' , 'AMap.MapType' , 'AMap.PolyEditor' , 'AMap.CircleEditor' , 'AMap.Geolocation' ], v: '1.4.4' });
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <div class="getlocation"> 定位 </div> <el-amap vid="amap" :plugin="plugin" class="amap-demo" :center="center"> </el-amap> <div class="toolbar"> <span v-if="loaded">location: lng = {{ lng }} lat = {{ lat }}</span> <span v-else>正在定位</span> </div> <script> export default { data() { let self = this; return { center: [121.59996, 31.197646], lng: 0, lat: 0, loaded: false, plugin: [ { pName: "Geolocation", //定位 events: { init(o) { // o 是高德地图定位插件实例 o.getCurrentPosition((status, result) => { if (result && result.position) { console.log(status, result); self.lng = result.position.lng; //设置经度 self.lat = result.position.lat; //设置维度 self.center = [self.lng, self.lat]; //设置中心坐标 self.loaded = true; self.$nextTick(); } }); } } } ] }; }, </script> <style> .getlocation{ margin-left:4rem; font-size:15px; font-weight: 500; margin-top:0.3rem; } .amap-demo { height: 15rem; margin-top: 0.3rem; } </style>
与vue2相比重大变化 全局变量 Vue 2.x 有许多全局 API 和配置,这些 API 和配置可以全局改变 Vue 的行为,比如说创建全局组件、声明全局指令。
虽然这种声明方式很方便,但它也会导致一些问题。从技术上讲,Vue 2 没有“app”的概念,我们定义的应用只是通过 new Vue()
创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例共享相同的全局配置 ,
Vue3中新的api
createapp
调用 createApp
返回一个应用实例,进行挂载
1 2 3 4 5 import { createApp } from 'vue' import MyApp from './MyApp.vue' const app = createApp(MyApp)app.mount('#app' )
其他全局api变化
2.x 全局 API
3.x 实例 API (app
)
Vue.config
app.config
Vue.config.productionTip
removed (见下方 )
Vue.config.ignoredElements
app.config.isCustomElement (见下方 )
Vue.component
app.component
Vue.directive
app.directive
Vue.mixin
app.mixin
Vue.use
app.use (见下方 )
利用这种方式,可以在应用之间共享全局组件和指令
1 2 3 4 5 6 7 8 9 10 11 12 13 import { createApp } from 'vue' import Foo from './Foo.vue' import Bar from './Bar.vue' const createMyApp = options => { const app = createApp(options) app.directive('focus' ) return app } createMyApp(Foo).mount('#foo' ) createMyApp(Bar).mount('#bar' )
上面代码中,createMyApp(Bar).mount(‘#bar’)Foo 和 Bar 实例及其后代中都可以使用 focus 指令
模版语法变化(Fragment片段) 在 2.x 中,不支持多根组件,当用户意外创建多根组件时会发出警告,因此,为了修复此错误,许多组件被包装在一个 <div>
中。
在 3.x 中,组件现在可以有多个根节点!但是,这确实要求开发者明确定义属性应该分布在哪里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //vue2 <template> <div> <header>...</header> <main>...</main> <footer>...</footer> </div> </template> //vue3 <template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer> </template>
生命周期函数变化 filter将不再被继续允许使用,鼓励使用computed或mothod替代filter功能
其他生命周期函数:
被弃用/替换:
beforeCreate -> setup()
created -> setup()
重命名的生命周期:
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
组合式API/composition 组合式API是vue中类似于react hook类似的功能,尤雨溪在关于此部分功能的考虑和征求意见稿的全文如下
https://vue3js.cn/vue-composition/
简言之,vue在两种情况下不太友好:
1.随着功能的增长,复杂组件的代码变得越来越难以阅读和理解。这种情况在开发人员阅读他人编写的代码时尤为常见。根本原因是 Vue 现有的 API 迫使我们通过选项组织代码 ,但是有的时候通过逻辑关系 组织代码更有意义。
目前vue缺少一种简洁且低成本的机制来提取和重用多个组件之间的逻辑。
2.来自大型项目开发者的常见需求是更好的 TypeScript 支持,不支持typescript意味着意味推断不够好。Vue 当前的 API 在集成 TypeScript 时遇到了不小的麻烦,其主要原因是 Vue 依靠一个简单的 this
上下文来暴露 property,我们现在使用 this
的方式是比较微妙的。
3.缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制。
Vue 现有的 API 在设计之初没有照顾到类型推导,这使适配 TypeScript 变得复杂。
composition核心函数:
1.setup函数
setup
就是将 Vue2.x 中的 beforeCreate
和 created
代替了,以一个 setup
函数的形式,可以灵活组织代码了。
setup
还可以 return 数据或者 template,相当于把 data
和 render
也一并代替了!
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template> <div>{{ count }} {{ object.foo }}</div> </template> <script> import { ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) // expose to template return { count, object } } } </script>
需要注意的是,setup函数中取消this,取消了 this
,取而代之的是 setup
增加了2个参数:props,组件参数和context,上下文信息
2.Reactive方法:
被 reactive
方法包裹后的 对象
就变成了一个代理对象,相当于 Vue2.x 中的 Vue.observable()
。也就可以实现页面和数据之间的双向绑定了。
这个包裹的方法是 deep
的, vue3.md 对所有嵌套的属性都生效。
3.ref方法
被 ref
方法包裹后的 元素
就变成了一个代理对象。一般而言,这里的元素参数指 基本元素
或者称之为 inner value
,如:number, string, boolean,null,undefined 等,object 一般不使用 ref
,而是使用上文的 reactive
。
也就是说 ref
一般适用于某个元素的;而 reactive
适用于一个对象。
4.isRef方法
判断一个对象是否 ref
代理对象。
1 const unwrapped = isRef(foo) ? foo.value : foo
5.toRefs 方法
将一个 reactive
代理对象打平,转换为 ref
代理对象,使得对象的属性可以直接在 template
上使用。
6.computed函数
与 Vue2.x 中的作用类似,获取一个计算结果。当然功能有所增强,不仅支持取值 get
(默认),还支持赋值 set
。
结果是一个 ref
代理对象,js中取值需要 .value
。
当 computed 参数使用 object 对象书写时,使用 get 和 set 属性。set 属性可以将这个对象编程一个可写的对象。
也就是说 computed
不仅可以获取一个计算结果,它还可以反过来处理 ref
或者 reactive
对象
7.readonly函数
使用 readonly
函数,可以把 普通 object 对象
、reactive 对象
、ref 对象
返回一个只读对象。
返回的 readonly 对象,一旦修改就会在 console 有 warning 警告。程序还是会照常运行,不会报错。
8.watch函数
watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。
监听不同的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import { defineComponent, ref, reactive, toRefs, watch } from "vue" ;export default defineComponent({ setup() { const state = reactive({ nickname : "xiaofan" , age : 20 }); setTimeout(() => { state.age++; }, 1000 ); watch( () => state.age, (curAge, preAge) => { console .log("新值:" , curAge, "老值:" , preAge); } ); return { ...toRefs(state), }; }, }); const year = ref(0 );setTimeout(() => { year.value++; }, 1000 ); watch(year, (newVal, oldVal) => { console .log("新值:" , newVal, "老值:" , oldVal); }); watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => { console .log("新值:" , curAge, "老值:" , preAge); console .log("新值:" , newVal,"老值:" , oldVal); });const state = reactive({ room: { id: 100 , attrs: { size: "140平方米" , type: "三室两厅" , }, }, }); watch( () => state.room, (newType, oldType) => { console .log("新值:" , newType, "老值:" , oldType); }, { deep : true } );
停止监听
1 2 3 4 5 6 7 8 const stopWatchRoom = watch(() => state.room, (newType, oldType) => { console .log("新值:" , newType, "老值:" , oldType); }, {deep :true }); setTimeout(() => { stopWatchRoom() }, 3000 )
9.watcheffect函数
watcheffect与watch的区别:
watchEffect 不需要手动传入依赖
watchEffect 会先执行一次用来自动收集依赖
watchEffect 无法获取到变化前的值, 只能获取变化后的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import { reactive, watchEffect } from 'vue' const state = reactive({ count: 0 , }) function increment ( ) { state.count++ } const renderContext = { state, increment, } watchEffect(() => { renderTemplate( `<button @click="increment">{{ state.count }}</button>` , renderContext ) })
自定义hook 约定这些「自定义 Hook」以 use 作为前缀,和普通的函数加以区分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import { ref, Ref, computed } from "vue" ;type CountResultProps = { count: Ref<number>; multiple: Ref<number>; increase: (delta?: number ) => void ; decrease: (delta?: number ) => void ; }; export default function useCount (initValue = 1 ): CountResultProps { const count = ref(initValue); const increase = (delta?: number): void => { if (typeof delta !== "undefined" ) { count.value += delta; } else { count.value += 1 ; } }; const multiple = computed(() => count.value * 2 ); const decrease = (delta?: number): void => { if (typeof delta !== "undefined" ) { count.value -= delta; } else { count.value -= 1 ; } }; return { count, multiple, increase, decrease, }; }
使用useCount这个hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template> <p>count: {{ count }}</p> <p>倍数: {{ multiple }}</p> <div> <button @click="increase()">加1</button> <button @click="decrease()">减一</button> </div> </template> <script lang="ts"> import useCount from "../hooks/useCount"; setup() { const { count, multiple, increase, decrease } = useCount(10); return { count, multiple, increase, decrease, }; }, </script>
Vue2.x 实现,分散在data
,method
,computed
等, 如果刚接手项目,实在无法快速将data
字段和method
关联起来,而 Vue3 的方式可以很明确的看出,将 count 相关的逻辑聚合在一起, 看起来舒服多了, 而且useCount
还可以扩展更多的功能。 项目开发完之后,后续还会写一篇总结项目中使用到的「自定义 Hooks 的文章」,帮助大家更高效的开发,
teleport 在子组件Header
中使用到Dialog
组件,我们实际开发中经常会在类似的情形下使用到 Dialog
,此时Dialog
就被渲染到一层层子组件内部,处理嵌套组件的定位、z-index
和样式都变得困难。 Dialog
从用户感知的层面,应该是一个独立的组件,从 dom 结构应该完全剥离 Vue 顶层组件挂载的 DOM;同时还可以使用到 Vue 组件内的状态(data
或者props
)的值。简单来说就是,即希望继续在组件内部使用Dialog
, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中 。 此时就需要 Teleport 上场,我们可以用<Teleport>
包裹Dialog
, 此时就建立了一个传送门,可以将Dialog
渲染的内容传送到任何指定的地方。
Suspense 在前后端交互获取数据时, 是一个异步过程,一般我们都会提供一个加载中的动画,当数据返回时配合v-if
来控制数据显示。 如果你使用过vue-async-manager
这个插件来完成上面的需求, 你对Suspense
可能不会陌生
Vue3.x 新出的内置组件Suspense
, 它提供两个template
slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容, 通过使用Suspense
组件进行展示异步渲染就更加的简单。:::warning 如果使用 Suspense
, 要返回一个 promise :::Suspense
组件的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <Suspense> <template #default> <async-component></async-component> </template> <template #fallback> <div> Loading... </div> </template> </Suspense> <<template> <div> <h4>这个是一个异步加载数据</h4> <p>用户名:{{user.nickname}}</p> <p>年龄:{{user.age}}</p> </div> </template> <script> import { defineComponent } from "vue" import axios from "axios" export default defineComponent({ setup(){ const rawData = await axios.get("http://xxx.xinp.cn/user") return { user: rawData.data } } }) </script>
slot变化 vue2中使用slot插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!-- 父组件中使用 --> <template slot="content" slot-scope="scoped"> <div v-for="item in scoped.data">{{item}}</div> <template> // 子组件 <slot name="content" :data="data"></slot> export default { data(){ return{ data:["走过来人来人往","不喜欢也得欣赏","陪伴是最长情的告白"] } } }
Vue3将slot
和slot-scope
进行了合并同意使用。 Vue3.0 中v-slot
:
1 2 3 4 5 6 7 8 9 <!-- 父组件中使用 --> <template v-slot:content="scoped"> <div v-for="item in scoped.data">{{item}}</div> </template> <!-- 也可以简写成: --> <template #content="{data}"> <div v-for="item in data">{{item}}</div> </template>
异步组件 Vue3 中 使用 defineAsyncComponent
定义异步组件,配置选项 component
替换为 loader
,Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template> <!-- 异步组件的使用 --> <AsyncPage /> </tempate> <script> import { defineAsyncComponent } from "vue"; export default { components: { // 无配置项异步组件 AsyncPage: defineAsyncComponent(() => import("./NextPage.vue")), // 有配置项异步组件 AsyncPageWithOptions: defineAsyncComponent({ loader: () => import(".NextPage.vue"), delay: 200, timeout: 3000, errorComponent: () => import("./ErrorComponent.vue"), loadingComponent: () => import("./LoadingComponent.vue"), }) }, } </script>
tree-shaking Vue3.x 在考虑到 tree-shaking
的基础上重构了全局和内部 API, 表现结果就是现在的全局 API 需要通过 ES Module
的引用方式进行具名引用
如vue中使用nextTick()
1 2 3 4 5 6 7 8 9 10 11 12 import Vue from "vue" Vue.nextTick(() => { ... }) import { nextTick } from "vue" nextTick(() => { ... })
类似的api变化
Vue.nextTick
Vue.observable
(用 Vue.reactive
替换)
Vue.version
Vue.compile
(仅限完整版本时可用)
Vue.set
(仅在 2.x 兼容版本中可用)
Vue.delete
(与上同)
Element-plus