一.transition路由切换动画

实现路由向左切换,向右切换的动画。

1.定义多个跳转路由

定义路由的深度,用于判断路由的方向
{path: "/",name: 'home',component: Home,meta: {depth: 1}
},
{path: "/list",name: 'list',component: List,meta: {depth: 2}
},
{path: "/detail/:id",name: 'detail',component: Detail,meta: {depth: 3}
},

2.监听路由的切换

@Watch("$route")
onRouteChange(to: Route, from: Route) {this.transitionName =to!.meta!.depth > from!.meta!.depth ? "slide-left" : "slide-right";
}

3.transition样式

.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {will-change: transform;transition: transform 350ms;position: absolute;overflow: hidden;
}.slide-right-enter,
.slide-left-leave-active {transform: translate(-100%, 0);
}.slide-left-enter,
.slide-right-leave-active {transform: translate(100%, 0);
}

二、插槽 - Slot

1.用法作用:在组件模板中占位,以复用组件的布局

默认slot使用

⼦组件⽤标签来确定渲染的位置,标签⾥⾯可以放DOM结构,当⽗组件使⽤的时候没有往插
槽传⼊内容,标签内DOM结构就会显示在⻚⾯

具名slot使用

⼦组件⽤v-slot来表示插槽的名字,不传为默认插槽
⽗组件中在使⽤时在默认插槽的基础上加上slot属性,值为⼦组件插槽v-slot的属性值

// 组件
<template><div class="slot-component"><div class="header"><slot name="header">我是默认头部文字</slot></div><div class="middle"><div class="left"><slot name="left">我是默认中间左边文字</slot></div><div class="right"><slot name="right">我是默认中间右边文字</slot></div></div><div class="footer"><slot name="footer">我是默认底部文字</slot></div></div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class SlotComponent extends Vue {}
</script>
// 页面使用
<template><slot-component><template v-slot:header>导航栏</template><template v-slot:left>侧边栏</template><template v-slot:right>右侧内容</template><template v-slot:footer>底部内容<router-link to="/home">回home</router-link></template></slot-component>
</template><script lang="ts">
import { Component, Vue} from "vue-property-decorator";
import SlotComponent from "../components/SlotComponent.vue";
@Component({components: { SlotComponent },
})

传参

// 组件
<template><div class="slot-component"><div class="header"><slot name="header">我是默认头部文字</slot></div><div class="middle"><div class="left"><slot name="left">我是默认中间左边文字</slot></div><div class="right"><slot name="right" :count="count">我是默认中间右边文字</slot></div></div><div class="footer"><slot name="footer">我是默认底部文字</slot></div></div>
</template>
<script lang="ts">
import { Component, Vue} from "vue-property-decorator";
@Component
export default class SlotComponent extend Vue {count: number = 10000;
}
</script >
// 页面使用
<template><slot-component><template v-slot:header>导航栏</template><template v-slot:left>侧边栏</template><template v-slot:right="slotProps">右侧内容<p>count = {{slotProps.count}}</p></template><template v-slot:footer>底部内容<router-link to="/home">回home</router-link></template></slot-component>
</template><script lang="ts">
import { Component, Vue} from "vue-property-decorator";
import SlotComponent from "../components/SlotComponent.vue";
@Component({components: { SlotComponent },
})
</script >

效果如图:

2.slot的实现原理

slot本质上是返回VNode的函数,⼀般情况下,Vue中的组件要渲染到⻚⾯上需要经过
template render function VNode DOM 过程。

⽐如⼀个带slot的组件

Vue.component('button-counter', {template: '<div> <slot>我是默认内容</slot></div>'
})
new Vue({el: '#app',template: '<button-counter><span>我是slot传⼊内容</span></button-counter>',components:{buttonCounter}
})

经过vue编译, 组件渲染函数会变成这样

(function anonymous(
) {with(this){return _c('div',[_t("default",[_v("我是默认内容")])],2)}
})

⽽这个_t就是slot渲染函数

function renderSlot (name,fallback,props,bindObject
) {// 得到渲染插槽内容的函数var scopedSlotFn = this.$scopedSlots[name];var nodes;// 如果存在插槽渲染函数,则执⾏插槽渲染函数,⽣成nodes节点返回// 否则使⽤默认值nodes = scopedSlotFn(props) || fallback;return nodes;
}

⽽scopedSlots其实就是递归解析各个节点, 获取slot

function resolveSlots (children,context) {if (!children || !children.length) {return {}}var slots = {};for (var i = 0, l = children.length; i < l; i++) {var child = children[i];var data = child.data;// remove slot attribute if the node is resolved as a Vue slot nodeif (data && data.attrs && data.attrs.slot) {delete data.attrs.slot;}// named slots should only be respected if the vnode was rendered in the// same context.if ((child.context === context || child.fnContext === context) &&data && data.slot != null) {// 如果slot存在(slot="header") 则拿对应的值作为keyvar name = data.slot;var slot = (slots[name] || (slots[name] = []));// 如果是tempalte元素 则把template的children添加进数组中,这也就是为什么你写的
template标签并不会渲染成另⼀个标签到⻚⾯if (child.tag === 'template') {slot.push.apply(slot, child.children || []);} else {slot.push(child);}} else {// 如果没有就默认是default(slots.default || (slots.default = [])).push(child);}}// ignore slots that contains only whitespacefor (var name$1 in slots) {if (slots[name$1].every(isWhitespace)) {delete slots[name$1];}}return slots
}

三、Mixin 混入

本质其实就是⼀个js对象,可以包含我们组件中任意功能选项,如data、components、methods、created、computed等等

我们一般将公共的功能呢或数据以对象的形式放到mixin中,其他组件或页面组件使用mixin对象时,就会将mixin组件中的功能和数据混入到使用的组件中。

  • 当组件存在与mixin对象相同的数据的时候,进⾏递归合并的时候组件的数据会覆盖mixin的数据

  • 如果相同数据为⽣命周期钩⼦的时候,会合并成⼀个数组,先执⾏mixin的钩⼦,再执⾏组件的钩⼦

1.mixin使用

// mixinimport { Vue, Component } from 'vue-property-decorator';@Component
export class CommonMixin extends Vue {mixinName: string = 'haiyang';enterTime: number = 0;mounted() {console.log('进入了mixin');this.enterTime = Date.now();}beforeDestroy() {console.log(`页面浏览了${Date.now() - this.enterTime}ms`);}
}
// 引入
<template><div>页面内容</div>
</template>
<script lang="ts">
import { Component, Vue, Mixins } from "vue-property-decorator";
import { CommonMixin } from "../mixins";
export default class Home extends Mixins(CommonMixin) {mounted() {console.log('进入了list页面')}beforeDestroy() {console.log('离开了list页面')}
};
</script>

2.四种策略以及实现原理

  • 优先递归处理 mixins,将子组件的mixins合并过来一同处理
  • 先遍历合并parent 中的key,调⽤mergeField⽅法进⾏合并,然后保存在变量options
  • 再遍历child,合并补上 parent 中没有的key,调⽤mergeField⽅法进⾏合并,保存在变量 options
  • 通过mergeField 函数进⾏了合并
export function mergeOptions (parent: Object,child: Object,vm?: Component
): Object {if (child.mixins) { // 判断有没有mixin 也就是mixin⾥⾯挂mixin的情况 有的话递归进⾏合并for (let i = 0, l = child.mixins.length; i < l; i++) {parent = mergeOptions(parent, child.mixins[i], vm)}}const options = {}let keyfor (key in parent) {mergeField(key) // 先遍历parent的key 调对应的strats[XXX]⽅法进⾏合并}for (key in child) {if (!hasOwn(parent, key)) { // 如果parent已经处理过某个key 就不处理了mergeField(key) // 处理child中的key 也就parent中没有处理过的key}}function mergeField (key) {const strat = strats[key] || defaultStratoptions[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调⽤strats中不同的⽅法进⾏合并// (不同的策略进行合并)}return options
}

其实主要的逻辑就是合并mixin和当前组件的各种数据, 细分为四种策略:

替换性策略 - 同名的props、methods、inject、computed会被后来者代替
strats.props =
strats.methods =
strats.inject =
strats.computed = function (parentVal: ?Object,childVal: ?Object,vm?: Component,key: string
): ?Object {if (!parentVal) return childVal // 如果parentVal没有值,直接返回childValconst ret = Object.create(null) // 创建⼀个第三⽅对象 retextend(ret, parentVal) // extend⽅法实际是把parentVal的属性复制到ret中if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中return ret
}
合并型策略- data, 通过set⽅法进⾏合并和重新赋值
strats.data = function(parentVal, childVal, vm) {return mergeDataOrFn(parentVal, childVal, vm)
};
function mergeDataOrFn(parentVal, childVal, vm) {return function mergedInstanceDataFn() {var childData = childVal.call(vm, vm) // 执⾏data挂的函数得到对象var parentData = parentVal.call(vm, vm)if (childData) {return mergeData(childData, parentData) // 将2个对象进⾏合并} else {return parentData // 如果没有childData 直接返回parentData}}
}
function mergeData(to, from) {if (!from) return to;var key, toVal, fromVal;var keys = Object.keys(from);for (var i = 0; i < keys.length; i++) {key = keys[i];toVal = to[key];fromVal = from[key];// 如果不存在这个属性,就重新设置if (!to.hasOwnProperty(key)) {set(to, key, fromVal);}// 存在相同属性,合并对象else if (typeof toVal =="object" && typeof fromVal =="object") {mergeData(toVal, fromVal);}}return to;
}
队列型策略- ⽣命周期函数和watch,原理是将函数存⼊⼀个数组,然后正序遍历依次执⾏
function mergeHook (parentVal: ?Array<Function>,childVal: ?Function | ?Array<Function>
): ?Array<Function> {return childVal? parentVal? parentVal.concat(childVal): Array.isArray(childVal)? childVal: [childVal]: parentVal
}
LIFECYCLE_HOOKS.forEach(hook => {strats[hook] = mergeHook
})
叠加性策略 - component、directives、filters,通过原型链进⾏层层的叠加
strats.components=
strats.directives=
strats.filters = function mergeAssets(parentVal, childVal, vm, key
) {var res = Object.create(parentVal || null);if (childVal) {for (var key in childVal) {res[key] = childVal[key];}}return res
}

四、filter 过滤器

1.使用

不改变原始值,返回加工过的值,是一个纯函数
vue3已经弃用,后面多用computed去实现过滤功能

// vue文件
<template v-slot:left>手机号<span> {{phone | phoneFilter}}</span>
</template>import { Component, Vue} from "vue-property-decorator";
import { phoneFilter } from "../filters";
@Component({components: { SlotComponent },filters: { phoneFilter }
})
export default class Home extends Vue() {readonly phone: string = '15829069561';};// filters
export const phoneFilter = (value: string) => {if (!value) return "";return value.replace(/(1[3-9]\d)\d{4}(\d{4})/, "$1****$2");
};

2.实现原理

  • 在编译阶段通过parseFilters将过滤器编译成函数调⽤
    1.串联过滤器则是⼀个嵌套的函数调⽤,
    2.前⼀个过滤器执⾏的结果是后⼀个过滤器函数的参数
function parseFilters (filter) {let filters = filter.split('|');let expression = filters.shift().trim() // shift()删除数组第⼀个元素并将其返回,该⽅法会更改原数组let i;if (filters) {for(i = 0;i < filters.length;i++){experssion = warpFilter(expression,filters[i].trim()) // 这⾥传进去的expression实际上是管道符号前⾯的字符串,即过滤器的第⼀个参数}}return expression
}
// warpFilter函数实现
function warpFilter(exp,filter){// ⾸先判断过滤器是否有其他参数const i = filter.indexof('(')if(i<0){ // 不含其他参数,直接进⾏过滤器表达式字符串的拼接return `_f("${filter}")(${exp})`}else{const name = filter.slice(0,i) // 过滤器名称const args = filter.slice(i+1) // 参数,但还多了 ‘)’return `_f('${name}')(${exp},${args}` // 注意这⼀步少给了⼀个 ')'}
}
  • 编译后通过调⽤resolveFilter函数找到对应过滤器并返回结果
export function resolveFilter(id){return resolveAsset(this.$options,'filters',id,true) || identity
}
// 因为我们找的是过滤器,所以在 resolveFilter函数中调⽤时 type 的值直接给的 'filters',实际这个函数还可以拿到其他很多东⻄
export function resolveAsset(options,type,id,warnMissing){ if(typeof id !== 'string'){ // 判断传递的过滤器id 是不是字符串,不是则直接返回return}const assets = options[type] // 将我们注册的所有过滤器保存在变量中// 接下来的逻辑便是判断id是否在assets中存在,即进⾏匹配if(hasOwn(assets,id)) return assets[id] // 如找到,直接返回过滤器// 没有找到,代码继续执⾏const camelizedId = camelize(id) // 万⼀你是驼峰的呢if(hasOwn(assets,camelizedId)) return assets[camelizedId]// 没找到,继续执⾏const PascalCaseId = capitalize(camelizedId) // 万⼀你是⾸字⺟⼤写的驼峰呢if(hasOwn(assets,PascalCaseId)) return assets[PascalCaseId]// 如果还是没找到,则检查原型链(即访问属性)const result = assets[id] || assets[camelizedId] || assets[PascalCaseId]// 如果依然没找到,则在⾮⽣产环境的控制台打印警告if(process.env.NODE_ENV !== 'production' && warnMissing && !result){warn('Failed to resolve ' + type.slice(0,-1) + ': ' + id, options)}// ⽆论是否找到,都返回查找结果return result
}
  • 执⾏结果作为参数传递给toString函数,⽽toString执⾏后,其结果会保存在Vnode的text属性
    中,渲染到视图
function toString(value){return value == null ? '' : typeof value === 'object' ? JSON.stringify(value,null,2)// JSON.stringify()第三个参数可⽤来控制字符串⾥⾯的间距: String(value)
}

五、plugin-插件就是指对Vue的功能的增强或补充。

1.使用

使用剪切板插件

  • install想要使用的插件(剪切板插件clipboard2)
  • 在入口文件引入并use
// main.ts
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);
  • 在文件中使用
<p @click="copyText('底部内容')" style="cursor: pointer;">
底部内容
</p>

2.实现一个自定义的plugin

  • 什么是插件? 如何编写⼀个插件?
MyPlugin.install = function (Vue, options) {// 1. 添加全局⽅法或 propertyVue.myGlobalMethod = function () {// 逻辑...}// 2. 添加全局资源Vue.directive('my-directive', {bind (el, binding, vnode, oldVnode) {// 逻辑...}...})// 3. 注⼊组件选项Vue.mixin({created: function () {// 逻辑...}...})// 4. 添加实例⽅法Vue.prototype.$myMethod = function (methodOptions) {// 逻辑...}
}
Vue.use(plugin, options);
  • Vue.use做了什么?

判断当前插件是否已经安装过, 防⽌重复安装
处理参数, 调⽤插件的install⽅法, 第⼀个参数是Vue实例.

// Vue源码⽂件路径:src/core/global-api/use.js
import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {Vue.use = function (plugin: Function | Object) {const installedPlugins = (this._installedPlugins || (this._installedPlugins
= []))if (installedPlugins.indexOf(plugin) > -1) {return this}// additional parametersconst args = toArray(arguments, 1)args.unshift(this)if (typeof plugin.install === 'function') {plugin.install.apply(plugin, args)} else if (typeof plugin === 'function') {plugin.apply(null, args)}installedPlugins.push(plugin)return this}
}

vue-transitionslotmixinfilterplugin相关推荐

  1. canvas java 上传截图_在Vue项目中使用html2canvas生成页面截图并上传

    使用方法 项目中引入 npm install html2canvas html代码 //html代码 js代码 // 引入html2canvas import html2canvas from 'ht ...

  2. vue实现文件上传功能

    代码如下所示: <!DOCTYPE html> <html lang="en"> <head><meta charset="UT ...

  3. win10 4步快速安装vue

    1 安装nvm-windows 在这里下载安装nvm,目的是在同一台电脑中管理多个 Node 版本 建议采取默认安装路径安装 2 安装nodejs 以管理员模式打开命令行,然后输入 nvm list ...

  4. 前端Vue学习之路(五)插件的使用

    vant插件使用 这里我们是用的语法是vue2.0 所以是 npm i vant -S 用法 1.按需引入的话 找到package.json文件 添加以下内容 plugins:[['import', ...

  5. 前端Vue学习之路(四)axios请求数据

    axios 1.增加新知识 2.旧方案 3.新方案(一) 4.为什么要用拦截器 (新方案二) 1.增加新知识 假如每个组件都引用axios,后期如果axios库不再维护了,那每个组件都要改动 所以封装 ...

  6. 前端Vue学习之路(二)-Vue-router路由

    Vue学习之路 (二) Vue-router(基础版) 一.增加静态路由 二.动态路由+路由嵌套+404页面 三. 编程式导航 四.命名路由 五.命名视图 六.重定向和起别名 1.重定向 2.起别名 ...

  7. 前端Vue学习之路(一)-初识Vue

    Vue学习之路 (一) 1.引言 2.更换npm国内镜像源 3.用npm下载Vue 4.Vue全家桶 5.使用命令创建项目 5.推荐插件 6.推荐网站 7.学习扩展 1.引言 先安装node.js环境 ...

  8. Vue 自定义权限指令

    前述 虽然VUE 定义了一些常用的指令,例如v-on.v-bind等,但是在我们实际开发的时候,还是会自己定义一些指令用于适应我们自己的业务. 实现 这里由于我的项目做了动态权限,页面的按钮也需要根据 ...

  9. vue响应式给数组中的对象添加新属性

    需要使用到vue的全局api $set(item,'newParam','value')方法 group: [ // 对象数组{ id: '1', name: '任务1' ,disable: fals ...

  10. Vue生命周期函数详解

    生命周期图 生命周期方法 var vm = new Vue({el: '#app',data: {msg:'ok'},methods: {show:function () {console.log(& ...

最新文章

  1. android edittext 默认不获取焦点
  2. 莹石云存储卡不兼容_继入股无锡好达之后,华为再度入股国产滤波器厂商德清华莹...
  3. 雨林木风爱好者GHOSTXP装机版_NTFS_SP3_2010_03
  4. AS3.0开始类库依赖出现了四种新语法
  5. CentOS 6.4下安装中文输入法
  6. 大数据开发笔记(七):Kafka分布式流式处理
  7. NVIDIA Nsight Systems 入门及使用
  8. java+widthstep_关于IplImage中widthstep的大小与width,nchannels等的关系的问题
  9. ubuntu串口调试工具RS485
  10. 小米运动蓝牙耳机使用说明书-如果第二次切换到配对状态
  11. SQL Server Select Top语句
  12. 【转】Android兼容性测试的一些坑
  13. 学习Matlab第三天——画3D图
  14. 从微信提示用户浏览器打开
  15. “人人都是产品经理”之歪门邪道
  16. google chrome
  17. blinker 第三方库_blinker 库开发注意事项
  18. [渝粤教育] 中央财经大学 人力资源管理 参考 资料
  19. 第18课:如何确认你的转型方向 · 上(图文篇)
  20. 企业运用人工智能的案例_如何打造企业私域流量及企业微信

热门文章

  1. linux 看usb 存储设备,找到哪个驱动器对应于Linux中的哪个USB大容量存储设备
  2. python测试自动化封装_python接口自动化学习笔记(封装获取测试数据方法)
  3. 网络技术安全开发安卓APP
  4. sam卡和sim卡区别_科普拍了拍你~PSAM卡\SIM与SAM卡有什么不同?
  5. UG NX 10 重新附着草图
  6. 《公安机关办理刑事案件电子数据取证规则》
  7. 如何区分本地用户和域用户?
  8. coco人体姿态估计标注软件
  9. 哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名
  10. 视频帧凸包检测 结果存入数据库