主要分为三个方面来优化
vue 代码层面的优化
webpack 配置层面的优化
基础的 Web 技术层面的优化
一, 代码层面的优化
1.1,v-if 和 v-show 区分使用场景
v-if 是 真正 的条件渲染, 因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建; 也是惰性的: 如果在初始渲染时条件为假, 则什么也不做 -- 直到条件第一次变为真时, 才会开始渲染条件块
v-show 就简单得多, 不管初始条件是什么, 元素总是会被渲染, 并且只是简单地基于 CSS 的 display 属性进行切换
所以, v-if 适用于在运行时很少改变条件, 不需要频繁切换条件的场景; v-show 则适用于需要非常频繁切换条件的场景
1.2,computed 和 watch 区分使用场景
computed: 是计算属性, 依赖其它属性值, 并且 computed 的值有缓存, 只有它依赖的属性值发生改变, 下一次获取 computed 的值时才会重新计算 computed 的值
watch: 更多的是「观察」的作用, 类似于某些数据的监听回调 , 每当监听的数据变化时都会执行回调进行后续操作
当我们需要进行数值计算, 并且依赖于其它数据时, 应该使用 computed, 因为可以利用 computed 的缓存特性, 避免每次获取值时, 都要重新计算
当我们需要在数据变化时执行异步或开销较大的操作时, 应该使用 watch, 使用 watch 选项允许我们执行异步操作 ( 访问一个 API ), 限制我们执行该操作的频率, 并在我们得到最终结果前, 设置中间状态. 这些都是计算属性无法做到的
1.3,v-for 遍历必须为 item 添加 key, 且避免同时使用 v-if
(1)v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时, 需要为每一项 item 设置唯一 key 值, 方便 vue.js 内部机制精准找到该条列表数据. 当 state 更新时, 新的状态值和旧的状态值对比, 较快地定位到 diff .
2)v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高, 如果每一次都需要遍历整个数组, 将会影响速度, 尤其是当之需要渲染很小一部分的时候, 必要情况下应该替换成 computed 属性
- // 推荐
- <ul>
- <li
- v-for="user in activeUsers"
- :key="user.id">
- {{ user.name }}
- </li>
- </ul>
- computed: {
- activeUsers: function () {
- return this.users.filter(function (user) {
- return user.isActive
- })
- }
- }
- // 不推荐
- <ul>
- <li
- v-for="user in users"
- v-if="user.isActive"
- :key="user.id">
- {{ user.name }}
- </li>
- </ul>
1.4, 长列表性能优化
Vue 会通过 Object.defineProperty 对数据进行劫持, 来实现视图响应数据的变化, 然而有些时候我们的组件就是纯粹的数据展示, 不会有任何改变, 我们就不需要 Vue 来劫持我们的数据, 在大量数据展示的情况下, 这能够很明显的减少组件初始化的时间, 那如何禁止 Vue 劫持我们的数据呢? 可以通过 Object.freeze 方法来冻结一个对象, 一旦被冻结的对象就再也不能被修改了
- export default {
- data: () => ({
- users: {}
- }),
- async created() {
- const users = await axios.get("/api/users");
- this.users = Object.freeze(users);
- }
- };
1.5, 事件的销毁
Vue 组件销毁时, 会自动清理它与其它实例的连接, 解绑它的全部指令及事件监听器, 但是仅限于组件本身的事件. 如果在 JS 内使用 addEventListene 等方式是不会自动销毁的, 我们需要在组件销毁时手动移除这些事件的监听, 以免造成内存泄露
- created() {
- addEventListener('click', this.click, false)
- },
- beforeDestroy() {
- removeEventListener('click', this.click, false)
- }
1.6, 图片资源懒加载
对于图片过多的页面, 为了加速页面加载速度, 所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载. 这样对于页面加载性能上会有很大的提升, 也提高了用户体验
在项目中使用 Vue 的 vue-lazyload 插件
- // 安装插件
- NPM install vue-lazyload --save-dev
- // 在入口文件 man.JS 中引入并使用
- import VueLazyload from 'vue-lazyload'
- // 然后再 vue 中直接使用 or 添加自定义选项
- Vue.use(VueLazyload)
- Vue.use(VueLazyload, {
- preLoad: 1.3,
- error: 'dist/error.png',
- loading: 'dist/loading.gif',
- attempt: 1
- })
- // 在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy , 从而将图片显示方式更改为懒加载显示
- <img v-lazy="/static/img/1.png">
1.7, 路由懒加载
Vue 是单页面应用, 可能会有很多的路由引入 , 这样使用 webpcak 打包后的文件很大, 当进入首页时, 加载的资源过多, 页面会出现白屏的情况, 不利于用户体验
如果我们能把不同路由对应的组件分割成不同的代码块, 然后当路由被访问的时候才加载对应的组件, 这样就更加高效了
这样会大大提高首屏显示的速度, 但是可能其他的页面的速度就会降下来
- const Foo = () => import('./Foo.vue')
- const router = new VueRouter({
- routes: [
- { path: '/foo', component: Foo }
- ]
- })
1.8, 第三方插件的按需引入
我们在项目中经常会需要引入第三方插件, 如果我们直接引入整个插件, 会导致项目的体积太大, 我们可以借助 babel-plugin-component , 然后可以只引入需要的组件, 以达到减小项目体积的目的
以下为项目中引入 element-ui 组件库为例
- // 首先, 安装 babel-plugin-component
- NPM install babel-plugin-component -D
- // 然后, 将 .babelrc 修改为:
- {
- "presets": [["es2015", { "modules": false }]],
- "plugins": [
- [
- "component",
- {
- "libraryName": "element-ui",
- "styleLibraryName": "theme-chalk"
- }
- ]
- ]
- }
- // 在 main.JS 中引入部分组件:
- import Vue from 'vue';
- import { Button, Select } from 'element-ui';
- Vue.use(Button)
- Vue.use(Select)
1.9, 优化无限列表性能
如果你的应用存在非常长或者无限滚动的列表, 那么需要采用 窗口化 的技术来优化性能, 只需要渲染少部分区域的内容, 减少重新渲染组件和创建 dom 节点的时间.
可以参考以下开源项目 vue-virtual-scroll-list 和 vue-virtual-scroller 来优化这种无限列表的场景的
1.10, 服务端渲染 SSR or 预渲染
服务端渲染是指 Vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成, 服务端形成的 HTML 片段直接返回给客户端这个过程就叫做服务端渲染
(1)服务端渲染的优点
更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取, 而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容, 所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容; 而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中), 所以搜索引擎爬取工具可以抓取渲染好的页面
更快的内容到达时间(首屏加载更快):SPA 会等待所有 Vue 编译后的 JS 文件都下载完成后, 才开始进行页面的渲染, 文件下载等需要一定的时间等, 所以首屏渲染需要一定的时间; SSR 直接由服务端渲染好页面直接返回显示, 无需等待下载 JS 文件及再去渲染等, 所以 SSR 有更快的内容到达时间
(2)服务端渲染的缺点
更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数, 这会导致一些外部扩展库需要特殊处理, 才能在服务端渲染应用程序中运行; 并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同, 服务端渲染应用程序, 需要处于 Node.JS server 运行环境;
更多的服务器负载: 在 Node.JS 中渲染完整的应用程序, 显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源, 因此如果你预料在高流量环境下使用, 请准备相应的服务器负载, 并明智地采用缓存策略
二, Webpack 层面的优化
2.1,Webpack 对图片进行压缩
在 vue 项目中除了可以在 webpack.base.conf.JS 中 url-loader 中设置 limit 大小来对图片处理, 对小于 limit 的图片转化为 base64 格式, 其余的不做操作. 所以对有些较大的图片资源, 在请求资源的时候, 加载会很慢, 我们可以用 image-webpack-loader 来压缩图片
- // 首先, 安装 image-webpack-loader
- NPM install image-webpack-loader --save-dev
- // 然后, 在 webpack.base.conf.JS 中进行配置:
- {
- test: /\.(PNG|jpe?g|gif|svg)(\?.*)?$/,
- use:[
- {
- loader: 'url-loader',
- options: {
- limit: 10000,
- name: utils.assetsPath('img/[name].[hash:7].[ext]')
- }
- },
- {
- loader: 'image-webpack-loader',
- options: {
- bypassOnDebug: true,
- }
- }
- ]
- }
2.2, 减少 ES6 转为 ES5 的冗余代码
Babel 插件会在将 ES6 代码转换成 ES5 代码时会注入一些辅助函数, 例如下面的 ES6 代码
class HelloWebpack extends Component{...}
这段代码再被转换成能正常运行的 ES5 代码时需要以下两个辅助函数
- babel-runtime/helpers/createClass // 用于实现 class 语法
- babel-runtime/helpers/inherits // 用于实现 extends 语法
在默认情况下, Babel 会在每个输出文件中内嵌这些依赖的辅助函数代码, 如果多个源代码文件都依赖这些辅助函数, 那么这些辅助函数的代码将会出现很多次, 造成代码冗余
为了不让这些辅助函数的代码重复出现, 可以在依赖它们时通过 require('babel-runtime/helpers/createClass') 的方式导入, 这样就能做到只让它们出现一次.
- // 首先, 安装 babel-plugin-transform-runtime
- NPM install babel-plugin-transform-runtime --save-dev
- // 然后, 修改 .babelrc 配置文件为
- "plugins": [
- "transform-runtime"
- ]
- // 所有在 package.JSON 里面依赖的包, 都会被打包进 vendor.JS 这个文件中.
- new webpack.optimize.CommonsChunkPlugin({
- name: 'vendor',
- minChunks: function(module, count) {
- return (
- module.resource &&
- /\.JS$/.test(module.resource) &&
- module.resource.indexOf(
- path.join(__dirname, '../node_modules')
- ) === 0
- );
- }
- }),
- // 抽取出代码模块的映射关系
- new webpack.optimize.CommonsChunkPlugin({
- name: 'manifest',
- chunks: ['vendor']
- })
- webpack + vue-loader ( vue-cli 的 webpack 模板已经预先配置好)
- Browserify + vueify
- Rollup + rollup-plugin-vue
- if (config.build.bundleAnalyzerReport) {
- var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
- webpackConfig.plugins.push(new BundleAnalyzerPlugin());
- }
- // 安装:
- NPM install compression --save
- // 添加代码逻辑:
- var compression = require('compression');
- var App = express();
- App.use(compression())
来源: http://www.jianshu.com/p/def55b3aef81