本文是自己 vue 项目实践中的一些总结,针对 Vue2 及相关技术栈,实践中版本为 2.3.3。
在开发前,我们要至少通读一遍 vue 官方文档和 API(看官方文档是最重要的,胜过看五十、一百篇博客), 英文阅读能力还行的建议阅读英文文档,中文文档内容会稍落后,还要通读相关的 vue-router、axios、vuex 等。
一般来说我们都是先利用 vue-cli 来搭建项目基本架构。
vue-cli 官方 temaplte 地址,我们选择 webpack 版本,建议看看其文档 vue-webpack-boilerplate 了解基本用法和项目配置等。
深入地了解 vue-cli 的 webpack 配置请查看 vue-cli#2.0 webpack 配置分析
vue-cli 虽然强大,但是它有很多个步骤要我们去选择配置,而实际上公司业务很多配置是固定的,比如我们公司规定了要安装 vue-router、要使用 Standard 风格 Eslint 等,还规定了必须使用 sass,这样在 vue-cli 配置完成后还必须要 npm install node-sass 和 sass-loader,还有 axios 等也是一定要安装的。所以不应该每次新建一个项目都去一步步选择 vue-cli 的那些配置然后还要去安装 sass 等,应该在 vue-cli 基础上根据公司自身的情况打造团队的脚手架,只需运行脚手架,就可以初始化整个项目。
建议在 src / 目录增加 views 或 pages 目录来存放对应路由的组件,添加 api 目录,根据项目情况增加 filters、vuex 等目录。components 目录存放公共组件或者全局组件。每个组件目录可以将图片等资源放在一起。组件的子组件目录建议命名为 children 放在父组件目录下。如 home 组件目录为 home/home.vue,子组件 banner 路径为 home/chldren/banner/banner.vue。
vue-webpack-boilerplate 文档中有静态资源处理的详细说明,但发现还有很多人都不知道,因此在这里稍微提一下。
vue-webpack-boilerplate 的项目结构中,我们有静态资源两个目录:src/assets 和 static/
assets 目录中的文件会被 webpack 处理,只支持相对路径形式,assets/logo.png 会被编译为./assets/logo.png,不支持 / assets/logo.png
在 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)解耦
解耦的本质就是将变化分离
一、组件功能单一
- // wrong
- <control-input type="number"></control-input>
- // right
- <control-number></control-number>
二、采用稳定的接口
- // wrong
- this.$parent.$parent.$refs['resource-image'].open()
- // right
- bus.$emit('open-resource-image')
三、处理好共享的部分
- 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': path.resolve(__dirname, '../src/pages'),
- 'components': path.resolve(__dirname, '../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
更多如设置端口等请点击上面的链接查看文档。
本文最重要的是文章中给出的一些链接,尤其是开发前须知章节中的链接,最好点进去通读一下。
来源: http://www.cnblogs.com/ang-/p/7082202.html