not a better man

前端技术

更新webpack4记录

webpack4发布到现在3个多月的时间,webpack的配置正在向parcelljs靠齐,尽量让用户少配置,不要把时间浪费到配置工具上来。废话不多说,我们一步步来使用webpack4来构建vue2.x 应用。

目标

利用webpack4 打包 vue+ vue-router 应用

1.实现缓存常驻(基本库能够在每次打包没有变化)。

2. 基于路由进行代码分割,实行代码基于路由懒加载

3. 在每次提交代码的时候 利用eslint进行代码的检查,

4. 在编写过程中,能够实现热更新,能够自动打开浏览器。

5.在开发过程中,利用开发服务对api请求使用代理,解决跨域问题

6. 利用jest + vue-test-utils测试框架进行测试

7. 部分UI单元测试采用puppteer测试,如用户的登录注册,各个端的模拟

8. 将每个文件分割成小于200kB的文件(在HTTP2协议中,多个小文件比大文件传输速度快,而且只有一个tcp连接)

本次开发,vue-template 我们使用 pug 模板,css的编写使用stylus预处理语言。现在让我们Let’s rock it.项目地址是https://gitee.com/lmz2423/vue-webpack4/

webpack的基础知识介绍

webpack作为前端开发构建工具的一个利器,因为其配置丰富,生态社区健康成长,有很多可用的第三方插件,而且一切资源都可以打包,深受广大web前端同志喜爱,当然配置丰富,就导致配置负责,优化空间大,这也是webpack的烦恼之一。webpack的官方博客见https://medium.com/webpack

webpack4的到来,目标之一是减少用户的配置,让开发者将更多的时间放在开发,webpack4默认是不需要配置文件的。但是我们还是可以自己编写配置文件,以便精细化处理。

我们先来介绍webpack的四个核心概念

  1. 入口(entry)
  2. 输出(output)
  3. loader
  4. 插件(plugins)

入口

入口起点指示webpack需要使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。

我们可以看一下下面的例子

module.exports = {
   entry: "./src/main.js"
}

 出口

output属性告诉webpack在哪里输出它所创建的bundles.以及如何命名这些文件,默认值为.dist 

const path = require('path');

module.exports = {
  entry: './main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: [main]-[hash].js'
  }
};

在上面的示例中,我们通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称

loader

loader让webpack能够去处理那些非javascript文件,loader可以将所有类型文件转换webpack能够处理的有效模块。在webpack的配置loader有两个目标

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use属性,表示进行转换时,应该使用哪个loader
const path = require('path');

const config = {
  output: {
    filename: './src/main.js'
  },
  module: {
    rules: [
      { test: /\.jpeg|png|gif/, use: 'file-loader' }
    ]
  }
};

module.exports = config;

 插件

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。如果我们想使用插件,我们只需要引入该插件,然后将它插入到plugins数组中。

const HtmlWebpackPlugin = require('html-webpack-plugin'); 
// 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

在webpack4中添加了模式(mode) 有两个属性分为development 或production

注意:

1.当我们使用development 时,会将process.env.NODE_ENV的值设置为development 并且启用 NamedChunksPlugin 和 NameModulesPlugin

2.会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPluginOccurrenceOrderPluginSideEffectsFlagPlugin 和 UglifyJsPlugin。

webpack的基本概念解释清楚了,如果我们想进一步理解可以访问https://webpack.docschina.org/

开始出发

项目地址在https://gitee.com/lmz2423/vue-webpack4

首先我们创建目录 vue-webpack4 .使用mkdir vue-webpack4命令 ,进入 vue-webpack4目录,利用npm init 初始化package.json。

在scripts属性中我们添加如下内容

"scripts": {
    "build": "rm -rf dist/ && webpack --config webpack.prd.js ",
    "server": "node app.js",
    "eslint": "eslint --fix 'src/**/*.vue' 'src/**/*.js'",
    "precommit-echo": "echo '正在检查代码格式..' && exit 0",
    "test": "jest"
  },

build命令是进行项目打包

server 是启动开发程序

eslint  是进行代码格式化检测

precommit-echo 是在commit的时候进行eslint 的提示语

test 是跑测试用例

我们将程序建立在src 目录下。

安装 vue vue-router

我们使用如下命令安装vue vue-router

cnpm install --save-dev vue vue-router

 安装webpack工具

在webpack4中我们需要安装webpack-cli 和webpack

安装命令如下

cnpm install -D webpack-cli webpack

建立三个文件 webpack.base.js ,webpack.dev.js, webpack.prd.js, webpack.base.js文件是公用的文件。webpak.dev.js是开发阶段的配置

webpack.prd.js是生产环境的配置

我们现在来想一想,在开发阶段和打包阶段,都需要做的事情是,

  • 一编译vue模板代码
  • 解析stylus代码
  • 解析模板中使用的pug代码
  • 解析es6
  • 上述的功能都可以提取到webpack.base.js 中

添加webpack.base.js内容

下载babel-core 和babel-loader和babel-preset-env 添加对es6的支持,命令如下

cnpm install --save-dev babel-core babel-loader babel-preset-env

建立.babelrc文件,添加如下内容

{
    "presets": [
        ["env",{
            "targets":{
                "browsers": ["last 2 versions", "safari >= 7","ie >= 10"]
            }
        }]
    ],
}

这样告诉babel编译的js支持所有浏览器的最新两个版本,safari支持到7,ie支持到10.

在webpack.base.js中添加对js解析,添加对pug文件的支持 ,添加对vue模板的支持,

代码如下

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const base ={
    module:{
        rules:[
            
            {
                test:/\.js$/,
                loader:'babel-loader',
                include:[
                    path.resolve(__dirname,"src")
                ]
            },
            {
                test:/\.vue$/,
                loader:"vue-loader",
                include:[
                    path.resolve(__dirname, "src")
                ]
            },
            {
                test:/\.pug$/,
                // 这条规则应用到Vue组件内的`<template lang="pug">`
                oneOf:[
                    {
                        resourceQuery: /^\?vue/,
                        use:['pug-plain-loader']
                    }
                ]
            },
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader',
                ]
            },
            {
                test:/\.styl(us)?$/,
                use:[
                    'vue-style-loader',
                    'style-loader',
                    'css-loader',
                    'stylus-loader'
                ]
            }
        ]
    },
    plugins:[
        new VueLoaderPlugin({
        }),
        new HtmlWebpackPlugin({
            filename:'index.html',
            template:'./src/index.html',
        })
    ]
}
module.exports = base

注意

1,vue-loader npm 从版本15开始需要添加插件,从上述的第二行代码可以看出来。

2. 我们编写entry 和 output入口,主要是在开发阶段,我们需要热更新代码,在生产环境不需要热更新代码。

webpack.dev.js内容

const baseConfig = require('./webpack.base.js')
const path = require('path')
const webpack = require('webpack');

const devConfig = {
    devtool: 'inline-source-map',
    mode:'development',
    entry:[
        'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',
        './src/main.js'
    ],
    output:{
        path:path.resolve(__dirname,'dist'),
        filename:'[name].[hash].bundle.js',
        publicPath:'/'
   },
    module:{
        rules:[
            {
                test:/\.(png|jpg|jpeg|gif)$/,
                loader:'file-loader',
            },
        ]
    },
    plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin(),
    ]
}

const merge = require('webpack-merge')

const config = merge(baseConfig,devConfig)


module.exports = config

从上述代码看出相对于webpack4之前,新增了mode 属性 ,mode属性有两个值一个是development ,一个是production,当我们将mode的值设置为development的时候,process.env.NODE_ENV的值设置为 development,此外当development的时候,会加快编译速度,提供有用的错误信息,具体的介绍见官方博客https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a

这个时候我们配置了开发的webpack文件,这个时候我们使webpackDevMiddleware结合express启动开发服务器。具体配置见下面代码

const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const opn = require('opn')

const app = express()
const config = require('./webpack.dev.js')
const compiler = webpack(config)
const port = 8080
const env = "" //配置哪个环境
const proxy = require('http-proxy-middleware')

const uri = 'http://localhost:' + port 
//热更新

const devMiddleware = webpackDevMiddleware(compiler,{
  publicPath:config.output.publicPath,
  noInfo:true,
})
//此中间件必须在最前面,不然不起作用
app.use(require('connect-history-api-fallback')())

app.use(devMiddleware)

app.use(require("webpack-hot-middleware")(compiler,{
    log:false,
    path: '/__webpack_hmr',
    heartbeat:3000
}))




//代理相关

const proxyTable = {
  '/rest/activity': {
    target: `http://${env}api-activity.vipkid.com.cn`,
    changeOrigin: true,
    logLevel: 'debug',
    pathRewrite: {
      '^/rest/activity': '/'
    }
  },
  '/rest/effect': {
    target: `http://${env}.com.cn`,
    changeOrigin: true,
    logLevel: 'debug',
    pathRewrite: {
      '^/rest/effect': '/'
    }
  },
  '/rest/learninggw': {
    target: `http://${env}.com.cn`,
    changeOrigin: true,
    logLevel: 'debug',
    pathRewrite: {
      '^/rest/learninggw': '/'
    }
  },
  '/rest/wechat': {
    target: `http://${env}.com.cn`,
    changeOrigin: true,
    logLevel: 'debug',
    pathRewrite: {
      '^/rest/wechat': '/'
    }
  }
}

Object.keys(proxyTable).forEach(context=>{
  const options = proxyTable[context]
  if(typeof options === 'string'){
    options = {
      target:options
    }
  }
  app.use(proxy(context, options))
})

const portfinder = require('portfinder')

portfinder.basePort = port

console.log('开始启动 dev server')


devMiddleware.waitUntilValid(()=>{
  portfinder.getPort((err, port)=>{
    if(err){
      console.log(err)
    }
    console.log("开始侦测" + uri+ '\n')
    opn(uri)
    app.listen(port)
  })
})

 webpack-hot-middleware是用来进行热更新的。opn是自动开启系统默认浏览器的。

针对api请求开启了代理。我们在terminal中输入npm run server ,运行效果如下

我们编辑src中的 Index.vue文件 ,看到修改文字和样式会自动更新,如下面视频所示

详细的代码见码云上的代码例子。

代码规范检查

项目开发都需要一份代码规范,如

  • 1缩进规范,
  • 2.函数没有返回值,不能使用return语句
  • 3.不能使用分号
  • 条件语句不能出现恒定不变的量
  • 不允许出现重复的case的标签

等等代码规范

这个时候我们可以使用eslint来进行规范检查,但是什么时候进行检查呢,如果开发过程中进行检查,那么会影响开发过程,由于大家使用的版本管理工具基本上是git ,git 在 add commit 都有触发的钩子函数,我们可以在commit之前运行eslint

首先 创建eslint规范

我们建立.eslintrc文件,eslintrc的规则可以见eslintrc官网。配置如下

{
    "root": true,
    "parser": "babel-eslint",
    "parserOptions": {
      "ecmaVersion": 6,
      "ecmaFeatures": {
        "experimentalObjectRestSpread": true
      },
      "sourceType": "module"
    },
    "globals":{
      "page": true,
      "browser": true,
      "jestPuppeteer": true
    },
    "env": {
      "es6": true,
      "jest": true
    },
    "plugins": [
      //插件,此插件用于识别文件中的js代码,没有MIME类型标识没有script标签也可以识别到,因此拿来识别.vue文件中的js代码
      "html"
    ],
    "rules": {
      //代码规范
      }
}

下载pre-commit npm 在package.json中添加pre-commit属性,代码如下

"pre-commit": [
    "precommit-echo",
    "eslint"
  ],

precmmit-echo 是scirpts中的命令值,输出正在检查代码规范,我们在Index.vue添加console.log(“xx”) 如下视频所示

到现在为止,我们可以通过eslint + pre-commit 检查代码规范了,pre-commit npm 只能在commit 之前进行代码规范检查,如果想在pre-push阶段,进行单元测试呢。这个时候,我们可以使用husky npm 包来进行命令的配置,具体的配置查看文档。

代码测试

代码测试框架有很多,如Karma, jasmine jest  Karma因为测试配置复杂,在本文不提,现在我们使用facebook 开源的测试框架 jest ,配合 vue-test-utils来进行组件测试,具体的配置见各自的官方文档。

发表评论