晴明的博客园 GitHub      CodePen      CodeWars     

[vue] vue 、vue-loader

生命周期

beforecreated:el 和 data 并未初始化
created:完成了 data 数据的初始化,el没有
beforeMount:完成了 el 和 data 初始化(模板插入)
mounted :完成挂载(渲染出真实dom)

data,props,computed,methods是在created之前beforecreate之后创建的

父组件与子组件的生命周期是同步的:
父组件created -> 子组件created -> 父组件mounted -> 子组件mounted

过渡

在进入/离开的过渡中,会有 6 个 class 切换。

  • v-enter:定义进入过渡的开始状态。在元素被插入时生效,在下一个帧移除。

  • v-enter-active:定义过渡的状态。在元素整个过渡过程中作用,在元素被插入时生效,在 transition/animation 完成之后移除。这个类可以被用来定义过渡的过程时间,延迟和曲线函数。

  • v-enter-to: 定义进入过渡的结束状态。在元素被插入一帧后生效 (于此同时 v-enter 被删除),在 transition/animation 完成之后移除。

  • v-leave: 定义离开过渡的开始状态。在离开过渡被触发时生效,在下一个帧移除。

  • v-leave-active:定义过渡的状态。在元素整个过渡过程中作用,在离开过渡被触发后立即生效,在 transition/animation 完成之后移除。这个类可以被用来定义过渡的过程时间,延迟和曲线函数。

  • v-leave-to: 定义离开过渡的结束状态。在离开过渡被触发一帧后生效 (于此同时 v-leave 被删除),在 transition/animation 完成之后移除。

createElement

// @returns {VNode}
		createElement(
			// {String | Object | Function}
			// 一个 HTML 标签字符串,组件选项对象,或者一个返回值类型为 String/Object 的函数,必要参数
			'div',
			// {Object}
			// 一个包含模板相关属性的数据对象
			// 这样,您可以在 template 中使用这些属性。可选参数。
			{
				// 和`v-bind:class`一样的 API
				'class': {
					foo: true,
					bar: false
				},
				// 和`v-bind:style`一样的 API
				style: {
					color: 'red',
					fontSize: '14px'
				},
				// 正常的 HTML 特性
				attrs: {
					id: 'foo'
				},
				// 组件 props
				props: {
					myProp: 'bar'
				},
				// DOM 属性
				domProps: {
					innerHTML: 'baz'
				},
				// 事件监听器基于 `on`
				// 所以不再支持如 `v-on:keyup.enter` 修饰器
				// 需要手动匹配 keyCode。
				on: {
					click: this.clickHandler
				},
				// 仅对于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。
				nativeOn: {
					click: this.nativeClickHandler
				},
				// 自定义指令。注意事项:不能对绑定的旧值设值
				// Vue 会为您持续追踪
				directives: [
					{
						name: 'my-custom-directive',
						value: '2',
						expression: '1 + 1',
						arg: 'foo',
						modifiers: {
							bar: true
						}
					}
				],
				// Scoped slots in the form of
				// { name: props => VNode | Array<VNode> }
				scopedSlots: {
					default: props => createElement('span', props.text)
				},
				// 如果组件是其他组件的子组件,需为插槽指定名称
				slot: 'name-of-slot',
				// 其他特殊顶层属性
				key: 'myKey',
				ref: 'myRef'
			},
			// {String | Array}
			// 子节点 (VNodes),由 `createElement()` 构建而成,
			// 或使用字符串来生成“文本节点”。可选参数。
			[
				'先写一些文字',
				createElement('h1', '一则头条'),
				createElement(MyComponent, {
					props: {
						someProp: 'foobar'
					}
				})
			]
		)

scoped

如果希望 scoped 样式中的一个选择器能够作用得'更深',例如影响子组件,可以使用 >>> 操作符:

<style scoped>
.a >>> .b { /* ... */ }
</style>

有些像 SASS 之类的预处理器无法正确解析 >>>。这种情况下你可以使用 /deep/ 操作符取而代之——这是一个 >>> 的别名,同样可以正常工作。
通过 v-html 创建的 DOM 内容不受作用域内的样式影响,但是仍然可以通过深度作用选择器来为他们设置样式。

scoped是以该级向下的,会污染下级,并不是每个级单独独立的.

data()

data 可以理解为react的state

数组

Vue 不能检测以下变动的数组:
当利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:
vm.items.length = newLength

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)

// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)

为了解决第二类问题,可以使用 splice:

example1.items.splice(newLength)

对象

Vue.set(object, key, value)

//实例
vm.$set(object, key, value)

有时想向已有对象上添加一些属性,例如使用 Object.assign()方法来添加属性。但是,添加到对象上的新属性不会触发更新。
在这种情况下可以创建一个新的对象,让它包含原对象的属性和新的属性:

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

其他

data()时将props的值赋给state,但是并不会跟随props变化而变化,可以用watch或者computed.

data(){}里以 _$ 开头的属性 不会 被 Vue 实例代理,
因为它们可能和 Vue 内置的属性、API 方法冲突。

比较复杂的数据,比如说[{},{}]这种结构,
比较粗暴可以试试将key改为动态的,不过并不是推荐这种方式.
推荐开启watch的deep:true,并且

	  this.stateData.splice(this.propsData.length);
          this.propsData.forEach((v, k) => {
            let obj = Object.assign({}, v);
            this.$set(this.stateData, k, obj);
          });

keep-live

Props:

  • include - 字符串或正则表达式。只有匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何匹配的组件都不会被缓存。

<keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
activated 和 deactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。
主要用于保留组件状态或避免重新渲染。

匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。
匿名组件不能被匹配。

<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
  <component :is="view"></component>
</keep-alive>

<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>

<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>

v-model 与 .sync

v-model

<input v-model="something">
//相当于
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

默认的props是value,默认的event是input,
可以通过model自定义

  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    // 这样就允许拿 `value` 这个 prop 做其它事了
    value: String
  },

如果要说v-model和.sync有什么区别的话,
那就是v-model默认绑的事件是input,
.sync默认绑的update,而且一般是update:xx,
而v-model还可以自定义event.
实现上来说感觉没什么区别

.sync

相当于省去了父组件传给子组件的改值回调方法

//父
<comp :foo.sync="bar"></comp>
//相当于
<comp :foo="bar" @update:foo="val => bar = val"></comp>

//子 需要手动执行
this.$emit('update:foo', newValue)

可以同时给多个值添加.sync

<comp v-bind.sync="{ foo: 1, bar: 2 }"></comp>

父组件传递对象/数组到子组件,子组件使用相应的浅复制,
为了方便更改两者建立了.sync,但是当两者成功建立时,如果建立的是子组件的浅复制,
那么父组件相应的数据也是对该浅复制的引用,那么就容易出现问题需要注意.

computed , watch

  1. 如果一个数据依赖于其他数据,那么把这个数据设计为computed的
  2. 如果需要在某个数据变化时做一些事情,使用watch来观察这个数据变化

一般用watch监测props的变化,同步state.

执行顺序为:
默认加载的时候先computed再watch,不执行methods;等触发某一事件后,则是:先methods再watch。

nextTick

可以在数据变化之后立即使用

Vue.nextTick(callback)

vm.$nextTick()

nextTick([callback,context])在下次 DOM 更新循环结束之后执行延迟回调。
可在修改数据之后立即使用这个方法,获取更新后的 DOM。
但在动画里似乎不那么好用.

props

props: {
    // 基础类型检测 (`null` 指允许任何类型)
    propA: Number,
    // 可能是多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数值且有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }

type 可以是下面原生构造器:

    String
    Number
    Boolean
    Function
    Object
    Array
    Symbol

type 也可以是一个自定义构造器函数,使用 instanceof 检测。

当props为boolean时,不传值只写属性传入也是true.

vue里不能直接通过props改变state,只可以初始化时赋值一次,与react不一样

v-if v-show v-for

v-for 优先级比 v-if 高。
v-if可用于控制组件销毁
v-show则是控制display:block/none

is

is 可以将某些dom绑定为需要的组件,主要用于遍历时动态变换

//template
  <div is="IButton" plain>plain</div>
  <div :is="xx" plain>plain</div>
  <component :is="xx" plain>plain</component>
  <component is="IButton" plain>plain</component>
  
//js
import IButton from '../components/Button.vue';
export default {
  data() {
    return {
      xx: IButton
    }
  },
  components: {
    IButton
  },
}

错误处理

全局的错误收集,可以在这里进行发送错误日志

 Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
}

组件的catch错误生命周期
errorCaptured(err: Error, vm: Component, info: string) => ?boolean

组件通信

父组件使用子组件方法,$ref,$child.
子组件使用父组件方法,$parent,props回调,$emit,$listeners.
兄弟组件通信,$emit发射,$on接收.
vue2.0开始去除了$dispatch()和$broadcast(),只能通过上面几种方式多绕几层,
也可以用一种bus的方式替代$dispatch()和$broadcast().
建议使用vuex.

provide / inject,支持父级往下传,但似乎不能正确获取到this,所以似乎传递函数不好用,传递数据因为只能被inject读取,也不是响应式的,所以价值也不大

vue-loader

Src 导入

如果希望分隔 .vue 文件到多个文件中,可以通过 src 属性导入外部文件:

<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>

需要注意的是 src 导入遵循和 require() 一样的规则,这意味着你相对路径需要以 ./ 开始,还可以从 NPM 包中直接导入资源,例如:

<!-- import a file from the installed "todomvc-app-css" npm package -->
<style src="todomvc-app-css/index.css">

在自定义块上同样支持 src 导入,例如:

<unit-test src="./unit-test.js">
</unit-test>

有作用域的 CSS

子组件的根元素

使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件有作用域的 CSS 和子组件有作用域的 CSS 的影响。
也就是会出现同名css,父级污染子级的情况。

深度作用选择器

如果希望 scoped 样式中的一个选择器能够作用得“更深”,
例如影响子组件,可以使用 >>> 操作符:

<style scoped>
.a >>> .b { /* ... */ }
</style>

上述代码将会编译成:

.a[data-v-f3f3eg9] .b { /* ... */ }

有些像 SASS 之类的预处理器无法正确解析 >>>。
这种情况下可以使用 /deep/ 操作符取而代之——这是一个 >>> 的别名,同样可以正常工作。

动态生成的内容

通过 v-html 创建的 DOM 内容不受作用域内的样式影响,但是仍然可以通过深度作用选择器来为他们设置样式。

CSS 作用域不能代替 class。

考虑到浏览器渲染各种 CSS 选择器的方式,当 p { color: red } 设置了作用域时 (即与特性选择器组合使用时) 会慢很多倍。如果你使用 class 或者 id 取而代之,比如 .example { color: red },性能影响就会消除。

在递归组件中小心使用后代选择器

对选择器 .a .b 中的 CSS 规则来说,如果匹配 .a 的元素包含一个递归子组件,则所有的子组件中的 .b 都将被这个规则匹配。

资源路径处理

vue-loader可是识别webpack的alias,特别是@?

url-loader 允许有条件将文件转换为内联的 base-64 URL (当文件小于给定的阈值),这会减少小文件的 HTTP 请求。如果文件大于该阈值,会自动的交给 file-loader 处理。

提取 CSS 到单个文件

可以通过extractCSS设置extract-text-webpack-plugin提取vue里的css。

options: {
  extractCSS: true
}

单元测试

      import Vue from 'vue' // 导入Vue用于生成Vue实例 
      import Hello from '@/components/Hello' // 导入组件 
      // 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite) 
      describe('Hello.vue', () => {
        // 每个describe块应该包括一个或多个it块,称为测试用例(test case) 
        it('should render correct contents', () => {
          const Constructor = Vue.extend(Hello) // 获得Hello组件实例 
          const vm = new Constructor().$mount() // 将组件挂在到DOM上 //断言:DOM中class为hello的元素中的h1元素的文本内容为Welcome to Your Vue.js App 
          expect(vm.$el.querySelector('.hello h1').textContent)
            .to.equal('Welcome to Your Vue.js App')
        })
      })

当把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

computed,watch里用箭头函数会出问题。

$emit和$listeners不存在父子间穿透的问题.

extend的component即使一直执行document.body.appendChild,也只会append一次,暂不知机制是什么...

项目内组件不能和已注册过的组件同名,也就是要注意export default { name: 'X' }导出的name,甚至可以不设置这个name,在dev-tools里会与文件名保持同名.

vue里的父子组件里同时用使用transition不知为何会抖.animation倒是不会.

vue里的stylus引入stylus文件,如果引入了其他css文件会报错.但是如果在js里引是可以的.
vue里的stylus使用url()会报错,但css可以,可能是因为stylus的url会走publicPath或者static的目录.


模板字符串的使用

vue :custom="`hello${var}`"

非受控组件容易出问题,
父子组件利用v-model或者.sync,父组件传递的数据是对象/数组的某键值,$emit的改变很可能会出问题,
因为Vue不能检测对象/数组键值的添加或删除.


用concat会丢失双向绑定


.self 等价 if (event.target !== event.currentTarget) return


.native 主要是给自定义的组件添加原生事件,不加.native可能@click会被组件识别为某种$listener,html则没有这个问题.


vue包含css打包 比 vue与css拆分打包 更大

posted @ 2017-12-13 19:58  晴明桑  阅读(270)  评论(0编辑  收藏  举报