本文是本身vue项目实践中的一些总结,针对Vue2及相关技术栈,实践中版本为2.3.3。javascript
在开发前,咱们要至少通读一遍vue官方文档和API(看官方文档是最重要的,赛过看五10、一百篇博客),英文阅读能力还行的建议阅读英文文档,中文文档内容会稍落后,还要通读相关的vue-router、axios、vuex等。css
通常来讲咱们都是先利用vue-cli来搭建项目基本架构。html
vue-cli官方temaplte地址,咱们选择webpack版本,建议看看其文档vue-webpack-boilerplate了解基本用法和项目配置等。前端
深刻地了解vue-cli的webpack配置请查看vue-cli#2.0 webpack 配置分析vue
vue-cli虽然强大,可是它有不少个步骤要咱们去选择配置,而实际上公司业务不少配置是固定的,好比咱们公司规定了要安装vue-router、要使用Standard风格Eslint等,还规定了必须使用sass,这样在vue-cli配置完成后还必需要npm install node-sass和sass-loader,还有axios等也是必定要安装的。因此不该该每次新建一个项目都去一步步选择vue-cli的那些配置而后还要去安装sass等,应该在vue-cli基础上根据公司自身的状况打造团队的脚手架,只需运行脚手架,就能够初始化整个项目。java
建议在src/目录增长views或pages目录来存放对应路由的组件,添加api目录,根据项目状况增长filters、vuex等目录。components目录存放公共组件或者全局组件。每一个组件目录能够将图片等资源放在一块儿。组件的子组件目录建议命名为children放在父组件目录下。如home组件目录为home/home.vue,子组件banner路径为home/chldren/banner/banner.vue。node
vue-webpack-boilerplate文档中有静态资源处理的详细说明,但发现还有不少人都不知道,所以在这里稍微提一下。webpack
vue-webpack-boilerplate的项目结构中,咱们有静态资源两个目录:src/assets和static/ios
assets目录中的文件会被webpack处理,只支持相对路径形式,assets/logo.png会被编译为./assets/logo.png,不支持/assets/logo.pnggit
在js中,咱们能够这样获取文件资源路径
require('./assets/logo.png')
如下带~前缀相似require效果
<img src="~assets/logo.png">
static目录中的静态资源不会被webpack处理,这里适合放一些外部不须要webpack处理的资源,build后的静态资源都会被放进这个目录。
关于vue组件化,360奇舞团前端工程师钟恒的pptVue.js实践 如何使用Vue2.0开发富交互式WEB应用写得很是好,本节内容也是出自其中。ppt中提到组件化带来的新问题:通讯、复用、耦合,以及如何解决。
1)props和events:props down,events up
2)函数调用:this.refs
3)组件树: $parent.$parent
4)共享state
5)eventbus
6)vue技术栈以外的如localstorage等
1)冗余:if、else if、else判断执行不一样的代码
if(this.type === 'editing') { // some editing code } else if(this.type === 'preview') { // some preview code } else if(this.type === 'present') { // some present code } else { // some base code }
2)包装:slots
// plugin-page.vue <div> <slot name="page"> i am a page </slot> </div> // present-plugin-page.vue <div class="PresetPluginPage"> <plugin-page ref="page"> <h1 slot="page"> i am a present page </h1> </plugin-page> </div> //output <div class="PresetPluginPage"> <div> <h1> i am a present page </h1> </div> </div>
3)继承:mixins
// define a mixin object var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // define a component that uses this mixin var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // -> "hello from mixin!"
1)组件耦合带来的问题:
2)解耦
解耦的本质就是将变化分离
1、组件功能单一
// wrong <control-input type="number"></control-input> // right <control-number></control-number>
2、采用稳定的接口
// wrong this.$parent.$parent.$refs['resource-image'].open() // right bus.$emit('open-resource-image')
3、处理好共享的部分
bindEvents (remove) { let method = remove ? 'removeEventListener' : 'addEventListener' window[method]('resize', this.handleResize) }
3)与服务端解耦
this.$http.get('/user/detail') .then(({body}) => { this.user = JSON.parse(body).data }, err => { console.error(err) }) user.detail().then(detail => this.detail = detail)
服务端与前端体系不一
同步异步转换
多服务端/跨域的代码
统一的错误处理代码
1)不要滥用vuex
使用Vuex并不意味着你须要将全部的状态放入Vuex。虽然将全部的状态放到Vuex会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。若是有些状态严格属于单个组件,最好仍是做为组件的局部状态。你应该根据你的应用开发须要进行权衡和肯定。
2)最好在根实例中注册store选项,该store实例会注入到根组件下的全部子组件中,子组件能够经过this.$store访问,当状态较多时使用建议mapState辅助函数。
3)polyfill
本次项目中使用了vuex,所以为兼容IE9等低版本,须引入promise的polyfill--es6-promise。npm install后在main.js:
import 'es6-promise/auto'
为了和后端进行数据交互,咱们通常引入axios库。在main.js中以下将其加入vue的原型中,这样能够在组件中经过this.$http来获取axios:
Object.defineProperty(Vue.prototype, '$http', { value: axios }) // 或者 Object.defineProperty(Vue.prototype, '$axios', { value: axios })
此次实践中未采用这种作法,而是建立了一个getData.js进行了统一管理:
import axios from 'axios' const getSomething = (param1,param2) => axios.get(url,{ params: { param1: param1, param2: param2 } }) export { getSomething }
在单文件组件中import getSomething方法再进行调用便可。
直接在main.js中import你下载的iconfont.css便可
常见的需求是开发环境须console,而线上环境不能够console。默认环境有'development'、'production'、'testing'三种。
if (process.env.NODE_ENV !== 'production') { console.log(data) }
数据模拟请求利用了mock.js,配置文档,不过这个只是简单的数据模拟,没有生成文档的功,更全面的文档、Mock.js、可视化、Rest、接口过渡、文档修改提醒、支持本地部署等功能可使用阿里RAP。
npm install mockjs安装后,可在/src/api目录下新建data.js,引入mockjs,后可在程序入口或api入口根据开发环境来引入data.js,下面是几个示例:
import Mock from 'mockjs' let data = Mock.mock({ 'list|1-10': [{ 'id|+1': 1 }] }) // mock一个数据 console.log(JSON.stringify(data, null, 4)) import Mock from 'mockjs' Mock.setup({ timeout: '300‐500' }) Mock.mock(/\/login/, { code: 0 }) // 拦截login请求,返回对象{ code: 0 } import Mock from 'mockjs' Mock.setup({ timeout: '300‐500' }) Mock.mock(sitemap.cms.banners, { results: [] }) // 拦截sitemap中cms.banners请求,返回对象{ results: [] }
可使用服务端渲染或者预渲染,预渲染webpack插件github地址。
实际项目中仍是不可避免地要修改webpack配置,若是不知道怎么改的话就去查看webpack的配置分析去进行修改。
要设置全局变量能够在build中的webpack.base.conf.js中配置externals,与module同级:
externals: { sitemap: 'sitemap' }
而后在eslinttrc.js的module.exports添加这样一个配置:
globals: { 'sitemap': false }
在这个项目中要根据环境(开发环境、测试环境、生产环境)的不一样加载不一样的sitemap.js,这个sitemap.js会暴露出一个全局的sitemap变量,sitemap变量是个由api地址构成的json对象。利用HtmlWebpackPlugin插件的option选项来实现。
在index.html中这样写:
<script src="<%= htmlWebpackPlugin.options.src %>"></script>
而后在build中的各自conf.js的HtmlWebpackPlugin设置不一样的src,如在开发环境中添加src那一行:
new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', src: '//dev.example.com/api/sitemap.js', inject: true })
在webpack.base.conf.js,vue-cli已经默认配置好了src目录的别名为@,建议配置src下一级目录的别名,这样能减小重复书写也更美观,以下添加src、pages、components别名:
resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), 'src': resolve('src'), 'pages': resolve('src/pages'), 'components': resolve('src/components') } }
可使用webpack插件image-webpack-loader来压缩处理图片。
实际就是添加多个入口js而后再修改相应配置,网上资料不少,一搜就知道了。
咱们有时候须要关闭某些代码检查,具体配置参见Configuring ESLint - ESLint中文,下面是常见的两个:
1)关闭eslint
/* eslint-disable */ alert('foo') /* eslint-enable */
2)关闭禁止new
/* eslint-disable no-new */
1)因为vue的追踪对象变化原理基于使用Object.defineProperty,在处理大量数据而且不须要追踪对象变化时,可经过Object.freeze(data)冻结对象达到优化数据渲染处理
2)vue-router路由懒加载。当打包构建应用时,javascript包会变得很是大,影响页面加载。若是咱们能把不一样路由对应的组件分割成不一样的代码块,而后当路由被访问的时候才加载对应组件,这样就更加高效了。
1)使用表驱动法来注册全局filter、指令等,如在src下新建filters目录,index.js中import全部全局过滤器:
import milliFormat from './milliFormat' import reverse from './reverse' export default { milliFormat, reverse }
而后在main.js中注册
import commonFiltes from './filters/index' Object.keys(commonFiltes).forEach(key => Vue.filter(key, commonFiltes[key]))
2)对于一些强耦合的组件如collapse和collapse-item,可使用$parent和$children来进行通讯,不必像elementUI同样本身实现组件的broadcast和dispatch,我还发现有UI库居然是使用bus来通讯的,这样致使同一个页面要是有两个collapse,就会互相影响。
3)在根组件上注册公共过滤器后,除了在“Mustache”语法中使用,还可在组件中经过this.$root.$options.filters.datetime(data)获取datetime过滤器。
npm run build --report进行打包大小分析,可视化地看到有什么地方须要优化。
build成功后有个tip提示你build后的文件须要部署在http服务器上,不能经过file协议打开。
咱们能够经过node-static来启动服务。能够写一个js配置文件经过node来启动,或者CLI中输入static dist(先安装node-static):
$ static dist serving "dist" at http://127.0.0.1:8080
更多如设置端口等请点击上面的连接查看文档。
本文最重要的是文章中给出的一些连接,尤为是开发前须知章节中的连接,最好点进去通读一下。