mixins

混入是一种对重复代码的组织方式,可以在多个组件间复用代码。

如果在项目中,在多个组件间有一段逻辑代码是共同的。那常见的处理方式是:

  • 每个组件都复制粘贴代码(显然这是最不好方式)
  • 将以共同的代码逻辑抽离成多个函数,并存储到util文件里,在组件使用时引入这个方法。
  • 直接将共同的代码剥离出来,并存储到你定义的mixin文件里,在组件使用时引入,并在实例选项中全局混入,或组件中局部混入。

后两种方式在思想上是一致的,但是mixin混入比方法抽离更为彻底,在混入中完全按照vue的代码组织方式,可以使用method或生命周期钩子函数,可以包含this对象等。而抽离成工具方法中,不能使用vue相关的选项属性,只能是将重复的逻辑代码自定义成函数形式。

 混入对象可以引用任何`vue`组件所能引用的东西,就好像它是组件本身一样

基本使用:

比如在项目中有几个组件都要获取用户信息,调用getUserInfo方法。则可以按下面这么操作:

// userInfoMixin.js
export const userInfoMixin = {
methods: {
getUserInfo() {
return fetch(`api/user/${this.userId}`)
.then(res => res.json())
}
}
}
// A组件内,先引入,再使用
import {userInfoMixin} from '.mixins/userInfoMixin' export default {
mixins: [userInfoMixin],
props: {
userId: {
type: number
}
}
data() {
return {
userInfo: null,
}
},
mounted() {
// 直接调用this打点调用混入的方法,就像使用自身定义的一样
this.getUserInfo()
.then(res => {
this.userInfo = res
})
}
}

但上面这个例子,我们只虽剥离一个共同的方法。在utils中经常类似这么操作。实际上混入可以使用vue组件的任意属性,所以上面的data中的userInfomounted中的方法调用代码都是重复的,所以可以按下面优化后,这样混入:

// userInfoMixin.js
export const userInfoMixin = {
data() {
return {
userInfo: null,
}
},
mounted() {
this.getUserInfo()
.then(res => {
this.userInfo = res
})
}
methods: {
getUserInfo() {
return fetch(`api/user/${this.userId}`)
.then(res => res.json())
}
}
}
// A组件内,先引入,再使用
import {userInfoMixin} from '.mixins/userInfoMixin'
export default {
mixins: [userInfoMixin],
props: {
userId: {
type: number
}
}
}

虽然混入使组件简化了很多,但是让追踪数据的来源变得复杂了。当决定将哪些代码放在混入对象中,哪些代码保留在组件中,你必须衡量这样做的代价和收益。

混入注意事项

如果混入对象和组件间有重复的选项,vue会根据重复的选项类型来区别对待

  • 对生命周期钩子函数的重复,会将它们添加到一个数组中,并会全部执行。
  • 对其它重复的数据、方法、计算属性等非生命周期函数,组件中的属性优先级更高,会覆盖混入的对应项。
//mixin
export const hi = {
mounted() {
console.log('hello from mixin!')
}
} //vue instance or component
new Vue({
el: '#app',
mixins: [hi],
mounted() {
console.log('hello from Vue instance!')
}
}); //当组件创建时,`mixin`混入中信息会先于组件中信息的输出。
> hello from mixin!
> hello from Vue instance!
//mixin
export const hi = {
methods: {
sayHello: function() {
console.log('hello from mixin!')
}
},
mounted() {
this.sayHello()
}
} //vue instance or component
new Vue({
el: '#app',
mixins: [hi],
methods: {
sayHello: function() {
console.log('hello from Vue instance!')
}
},
mounted() {
this.sayHello()
}
}) // 组件内的方法覆盖了`mixin`的同名方法。
> hello from Vue instance!
> hello from Vue instance!

混入内对象的命名规范

有时候会特意用到这种冲突的覆盖,但有时需要避免这种冲突覆盖,在vue官方代码风格指南中建议对于混入的私有属性(不应该在在混入之外在使用的方法、数据或计算属性),应该在它们名称前面添加前缀$_以示区分。比如上面会被覆盖的方法可以命名成$_sayHello。这在开发自定义组件或插件时极为重要,因为用户在使用插件的项目中,可能会使用相同的属性名称,如果重名会造成BUG。

混入 mixin 的源码解读

其实源码中mixin原理并不复杂,只是将mixin中的对象与vue自身的options属性合并在一起。复杂是需要判断混入对象中的各个属性,根据属性不同采用不用的合并策略。

// 文件位置: vue/src/core/index
// vue实例初始化全局API
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)
// 文件位置: vue/src/core/goloba-api/index
import { initMixin } from './mixin'
initMixin(Vue)
// 文件位置:vue/src/core/global-api/mixin
import { mergeOptions } from '../util/index' export function initMixin (Vue) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
// mergeOptions方法在:vue/src/core/util/options
export function mergeOptions () {
// ...其它代码
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
// 如果组件parent内已经处理过的属性,则不再处理
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
} // 不同的全并策略
strats.data = function (){ /* do something */}
strats.watch = function (){ /* do something */}
strats.props =
strats.methods =
strats.inject =
strats.computed = function (){ /* do something */} // 对于component / directive / filter 等模板依赖相关的属性的混入策略
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets
}) // 对于生命周期钩子的混入策略
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
/**
* Hooks are merged as arrays.
*/
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
const res = childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
return res
? dedupeHooks(res)
: res
} function dedupeHooks (hooks) {
const res = []
for (let i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i]) // 推入数组中
}
}
return res
}

vue-learning:21 - js - mixins的更多相关文章

  1. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║Vue基础:JS面向对象&字面量& this字

    缘起 书接上文<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十四 ║ VUE 计划书 & 我的前后端开发简史>,昨天咱们说到了以我的经历说明的web开发经历的 ...

  2. vue.js mixins 使用

    export default { data () { return { } }, created () { }, methods: { arrayContain (array, obj) { for ...

  3. vue中config/index.js:配置的详细理解

    当我们需要和后台分离部署的时候,必须配置config/index.js: 用vue-cli 自动构建的目录里面  (环境变量及其基本变量的配置) var path = require('path') ...

  4. iosselect:一个js picker项目,在H5中实现IOS的select下拉框效果

    具体文档和demo可以访问github:https://github.com/zhoushengmufc/iosselect 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的pi ...

  5. Deep learning:五十一(CNN的反向求导及练习)

    前言: CNN作为DL中最成功的模型之一,有必要对其更进一步研究它.虽然在前面的博文Stacked CNN简单介绍中有大概介绍过CNN的使用,不过那是有个前提的:CNN中的参数必须已提前学习好.而本文 ...

  6. iosselect:一个js picker项目,在H5中实现IOS的下拉效果

    iosselect是在webapp下的一个picker组件,可以轻松实现各类选择器效果.比如地区选择 时间选择 日期选择等. 下面是一个地址选择器demo截图,可以访问:http://zhoushen ...

  7. Atom 编辑器安装 linter-eslint 插件,并配置使其支持 vue 文件中的 js 格式校验

    安装方式有如下几种. 1.最常用的安装方式. # 进入atom插件文件夹 cd ~/.atom/packages/ # git clone 插件源文件 git clone https://github ...

  8. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十二║Vue实战:个人博客第一版(axios+router)

    前言 今天正式开始写代码了,之前铺垫了很多了,包括 6 篇基础文章,一篇正式环境搭建,就是为了今天做准备,想温习的小伙伴可以再看看<Vue 基础入门+详细的环境搭建>,内容很多,这里就暂时 ...

  9. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十三║Vue实战:Vuex 其实很简单

    前言 哈喽大家周五好,马上又是一个周末了,下周就是中秋了,下下周就是国庆啦,这里先祝福大家一个比一个假日嗨皮啦~~转眼我们的专题已经写了第 23 篇了,好几次都坚持不下去想要中断,不过每当看到群里的交 ...

随机推荐

  1. 2019-8-31-asp-dotnet-core-支持客户端上传文件

    title author date CreateTime categories asp dotnet core 支持客户端上传文件 lindexi 2019-08-31 16:55:58 +0800 ...

  2. Leetcode783.Minimum Distance Between BST Nodes二叉搜索树结点最小距离

    给定一个二叉搜索树的根结点 root, 返回树中任意两节点的差的最小值. 示例: 输入: root = [4,2,6,1,3,null,null] 输出: 1 解释: 注意,root是树结点对象(Tr ...

  3. 【Leetcode链表】旋转链表(61)

    题目 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输出: ...

  4. 在centos搭建git服务器时,不小心把/home/git目录删除了,我是怎么恢复的

    在centos搭建git服务器时,不小心把/home/git目录删除了,我是怎么恢复的 在删除掉/home/git目录后,每次 git push提交时,都让填写密码,烦 第一步:在本地找到id_rsa ...

  5. 实现一个简易的promise

    //promise里面只有三个状态,且三个状态的转换形式有两种 //由pending转换为fulfilled,由pending转换为rejected //Promise的构造函数参数是一个函数,函数的 ...

  6. Django框架form表单配合ajax注册

    总结一下,其实form组件的主要功能如下: 生成页面可用的HTML标签 对用户提交的数据进行校验 保留上次输入内容 下面是写的登录页面的实例 1:views视图中的代码 # 注册页面 def regi ...

  7. [asp.net]登录协同工作平台安全解决方式

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/david_520042/article/details/25372207 [摘要]公司领导说登录验证 ...

  8. HTTP Status 500 - java.lang.ClassNotFoundException: org.apache.jsp.register_jsp

    你搜一下你的页面中是不是有<!---->的注释 去掉就好了 改成jsp的注释 1).JSP页面中的HTML注释 JSP页面中的HTML注释使用“<!—”和“-->”创建,它的具 ...

  9. [牛腩]如何关闭.net framework4.0的请求验证 标签: 发布 2015-07-31 09:27 887人阅读 评论(38)

    敲牛腩的时候,点击运行提示:从客户端中检测到有潜在危险的 Request.Form 值,感觉自己代码敲的并没有问题,于是开始各种查,下面分享一下我对此进行的研究. 为什么会报这个错误? 在 Web 应 ...

  10. 在web.xml中配置SpringMVC

    代码如下 <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.spr ...