本文是vue源码贡献值Chris Fritz在公共场合的一场分享,以为分享里面有很多东西值得借鉴,如今把大神的ppt在这里翻译一下,但愿跟朋友们共同窗习。原文:https://www.haorooms.com/post/vue_7secretvue
这一点我在项目中也是这么写的。例若有请求须要再也没初始化的时候就执行一次,而后监听他的变化,不少人这么写:webpack
created(){ this.fetchPostList() }, watch: { searchInputValue(){ this.fetchPostList() } }
上面的这种写法咱们能够彻底以下写:程序员
watch: { searchInputValue:{ handler: 'fetchPostList', immediate: true } }
通常状况下,咱们组件以下写:web
import BaseButton from './baseButton' import BaseIcon from './baseIcon' import BaseInput from './baseInput' export default { components: { BaseButton, BaseIcon, BaseInput } } <BaseInput v-model="searchText" @keydown.enter="search" /> <BaseButton @click="search"> <BaseIcon name="search"/></BaseButton>
步骤通常有三部,vue-router
第一步,引入、vuex
第二步注册、app
第三步才是正式的使用,函数
这也是最多见和通用的写法。可是这种写法经典归经典,好多组件,要引入屡次,注册屡次,感受很烦。post
咱们能够借助一下webpack,使用 require.context() 方法来建立本身的(模块)上下文,从而实现自动动态require组件。学习
思路是:在src文件夹下面main.js中,借助webpack动态将须要的基础组件通通打包进来。
代码以下:
import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/camelCase' // Require in a base component context const requireComponent = require.context( ‘./components’, false, /base-[\w-]+\.vue$/ ) requireComponent.keys().forEach(fileName => { // Get component config const componentConfig = requireComponent(fileName) // Get PascalCase name of component const componentName = upperFirst( camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')) ) // Register component globally Vue.component(componentName, componentConfig.default || componentConfig) })
这样咱们引入组件只须要第三步就能够了:
<BaseInput v-model="searchText" @keydown.enter="search" /> <BaseButton @click="search"> <BaseIcon name="search"/> </BaseButton>
对于vuex,咱们输出store以下写:
import auth from './modules/auth' import posts from './modules/posts' import comments from './modules/comments' // ... export default new Vuex.Store({ modules: { auth, posts, comments, // ... } })
要引入好多modules,而后再注册到Vuex.Store中~~
精简的作法和上面相似,也是运用 require.context()读取文件,代码以下:
import camelCase from 'lodash/camelCase' const requireModule = require.context('.', false, /\.js$/) const modules = {} requireModule.keys().forEach(fileName => { // Don't register this file as a Vuex module if (fileName === './index.js') return const moduleName = camelCase( fileName.replace(/(\.\/|\.js)/g, '') ) modules[moduleName] = { namespaced: true, ...requireModule(fileName), } }) export default modules
这样咱们只需以下代码就能够了:
import modules from './modules' export default new Vuex.Store({ modules })
这一点,关于vue的引入,我以前在vue项目重构技术要点和总结中也说起过,能够经过require方式或者import()方式动态加载组件。
{ path: '/admin', name: 'admin-dashboard', component:require('@views/admin').default }
或者
{ path: '/admin', name: 'admin-dashboard', component:() => import('@views/admin') }
加载路由。
下面这个场景真的是伤透了不少程序员的心...先默认你们用的是Vue-router来实现路由的控制。 假设咱们在写一个博客网站,需求是从/post-haorooms/a,跳转到/post-haorooms/b。而后咱们惊人的发现,页面跳转后数据居然没更新?!缘由是vue-router"智能地"发现这是同一个组件,而后它就决定要复用这个组件,因此你在created函数里写的方法压根就没执行。一般的解决方案是监听$route的变化来初始化数据,以下:
data() { return { loading: false, error: null, post: null } }, watch: { '$route': { handler: 'resetData', immediate: true } }, methods: { resetData() { this.loading = false this.error = null this.post = null this.getPost(this.$route.params.id) }, getPost(id){ } }
bug是解决了,可每次这么写也太不优雅了吧?秉持着能偷懒则偷懒的原则,咱们但愿代码这样写:
data() { return { loading: false, error: null, post: null } }, created () { this.getPost(this.$route.params.id) }, methods () { getPost(postId) { // ... } }
解决方案:给router-view添加一个惟一的key,这样即便是公用组件,只要url变化了,就必定会从新建立这个组件。
<router-view :key="$route.fullpath"></router-view>
注:我我的的经验,这个通常应用在子路由里面,这样才能够不避免大量重绘,假设app.vue根目录添加这个属性,那么每次点击改变地址都会重绘,仍是得不偿失的!
场景以下:
(Emitted value instead of an instance of Error) Error compiling template: <div></div> <div></div> - Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
模板中div只能有一个,不能如上面那么平行2个div。
例如以下代码:
<template> <li v-for="route in routes" :key="route.name" > <router-link :to="route"> {{ route.title }} </router-link> </li> </template>
会报错!
咱们能够用render函数来渲染
functional: true, render(h, { props }) { return props.routes.map(route => <li key={route.name}> <router-link to={route}> {route.title} </router-link> </li> ) }
当咱们写组件的时候,一般咱们都须要从父组件传递一系列的props到子组件,同时父组件监听子组件emit过来的一系列事件。举例子:
//父组件 <BaseInput :value="value" label="密码" placeholder="请填写密码" @input="handleInput" @focus="handleFocus> </BaseInput> //子组件 <template> <label> {{ label }} <input :value="value" :placeholder="placeholder" @focus=$emit('focus', $event)" @input="$emit('input', $event.target.value)" > </label> </template>
这样写很不精简,不少属性和事件都是手动定义的,咱们能够以下写:
<input :value="value" v-bind="$attrs" v-on="listeners" > computed: { listeners() { return { ...this.$listeners, input: event => this.$emit('input', event.target.value) } } }
$attrs包含了父做用域中不做为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定,而且能够经过 v-bind="$attrs" 传入内部组件。
$listeners包含了父做用域中的 (不含 .native 修饰器的) v-on 事件监听器。它能够经过 v-on="$listeners" 传入内部组件。