基于Webpack4搭建vue项目

768 阅读8分钟

虽然掘金里面已经很多关于这类型的文章,但是我最近在学习自己搭建,所以还是得写下,并且将一些搭建中遇到的问题也讲一下

一、webpack概念

首先自己去思考下每个概念是什么, 然后再对比下官方的解释,看下与自己想的是否大致相同,当然官方由于存在翻译差异性的问题所以也不一定就是解释的非常好的

我:webpack是一个静态资源打包工具 (10分)

官方:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

entry 入口

我:entry 指示以哪个文件作为构建依赖关系图的入口 (70分)

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

output 出口

我:output 告诉webpack打包生成的文件放在那里 (70分)

官方:output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

loader

我:默认情况下,webpack只能识别js,如果想要webpack去加载其他类型的文件,需要用到对应的loader进行解析 (70分)

官方:loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。 本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

plugins 插件

我:plugins插件可以实现更多的功能,来简化我们的开发过程 (60分)

官方:loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

模式

我: ...不知道 (0分)

官方:通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化

二、webpack基础构建

好啦~讲了一堆概念该动手实践啦

npm init -y //初始化项目
npm install
npm i webpack webpack-cli -D 下载相关webpack包

新建几个文件夹

src -- 源代码

src/components、 src/assets/image 、 src/index.js 入口

console.log('hello')

dist -- 生成文件路径

build -- webpack配置文件

index.html -- html模板

<!DOCTYPE html>
<html>
  <head>
    <title>Webpack Vue Demo</title>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

在build文件夹下创建webpack.base.conf.js、webpack.dev.conf.js、webpack.prod.conf.js文件,build.js

其中,webpack.base.conf.js 是最基础的打包配置,是开发环境和生产环境都要用到的配置。webpack.dev.conf.js 就是在开发环境要使用的配置。webpack.prod.conf.js 就是在生产环境要使用的配置了,build.js 是通过 Node 接口进行打包的脚本。 参考Russ_Zhonglv-3的build.js

webpack.base.conf.js

const path = require('path')
module.exports = {
    entry: {
        bundle: path.resolve(__dirname,'../src/index')
    },
    output: {
        filename: '[name].[hash].js',
        path: path.resolve(__dirname, '../dist')
    },
    module: {},
    plugins: []

}

开发环境和生产环境的配置需要用到 webpack-merge 去合并基础打包配置

webpack.dev.conf.js 开发环境配置需要用到 webpack-dev-server去启动 webpack-dev-server主要是启动了一个使用express的Http服务器。它的作用主要是用来伺服资源文件。此外这个Http服务器和client使用了websocket通讯协议,原始文件作出改动后,webpack-dev-server会实时的编译,但是最后的编译的文件并没有输出到目标文件夹,你启动webpack-dev-server后,你在目标文件夹中是看不到编译后的文件的,实时编译后的文件都保存到了内存当中。因此很多同学使用webpack-dev-server进行开发的时候都看不到编译后的文件

const merge = require("webpack-merge");
const baseConfig = require("./webpack.base.conf")
module.exports = merge(baseConfig, {
    mode: "development",
    devtool: "inline-source-map",
    devServer: {
        contentBase: path.resolve(__dirname, "../dist"),
        open: true
    }
})

webpack.prod.conf.js

const merge = require("webpack-merge");
const baseConfig = require("./webpack.base.conf")
module.exports = merge(baseConfig, {
    mode: "production",
    devtool: "source-map"
})

build.js 这个脚本用于构建生产环境,开发环境基于 webpack-dev-server 搭建,不写脚本。 接下来,写入我们的打包脚本,通过 Node 调用 Webpack 进行打包。

const webpack = require('webpack')
const config = require('./webpack.prod.conf')

webpack(config, (err, stats) => {
    if(err || stats.hasErrors) {
        console.log(err)
        return
    }
    console.log(stats.toString({
        chunks: false,
        colors: true
    }))
})

其实现在就可以尝试打包一下

在package.json中加入

"scripts": {
   "build": "node build/build.js",
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
  }

然后在命令行 输入 npm run build,

生成后dist文件夹多出了一个打包后的js文件

如何自动得生成html文件并引入生成的js文件,需要使用到一个插件 html-webpack-pulgin, 自行npm安装

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../index.html")
        })
    ]
}

这时候你可以继续打包一下 , 发现dist文件夹多出了一个html文件,但是上一次打包的js文件还在。 如何在每次打包的时候都清空一下dist呢?在webpack.prod.conf.js引入clean-webpack-plugin 插件, 这里需要强调下引入clean-webpack-plugin的时候需要用到结构赋值,可能是版本的问题

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const path = require("path")
module.exports = {
    plugins: [
        new CleanWebpackPlugin({
            root: path.resolve(__dirname, "../"),
            verbose: true, //就是控制台打印日志,默认是true
            dry: false //为false是删除文件夹的,为true是不删除的,默认值是false
        })
    ]
}

接着我们对常用的module进行配置,在webpack.base.conf.js文件夹中

 module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              esModule: false,
              name: "img/[name].[hash].[ext]",
              limit: 10000,
            }
          }
        ]
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ["file-loader"]
      },
      {
        test: /\.css$/,
        use:["style-loader", "css-loader"]
      }
    ]
  }

url-loader:对未设置或者小于limit设置的图片进行转换,以base64的格式被img的src所使用;而对于大于limit byte的图片用file-loader进行解析。

file-loader:可以用来帮助webpack打包处理一系列的图片文件,当webpack对项目代码进行打包构建时,遇到图片文件的时候,会去webpack的配置文件中,查找module节点,看看有没有对应的配置节点去处理图片的打包,发现图片文件是用file-loader进行打包的,就把图片文件交给file-loader进行打包,file-loader会首先将图片移动到出口文件夹下(我的项目时在bundle中),并且随机生成一个hash值,当做图片的名字;并将打包后的图片名称返回给require函数;

css-loader:作用是帮我们分析出各个css文件之间的关系,把各个css文件合并成一段css;

style-loader:作用是将css-loader生成的css代码挂载到页面的header部分

引入bable-loader,为了让我们的js代码兼容更多环境

需要安装 babel-loader @babel/core @babel/preset-env 然后在base.conf.js中加入对应的rule

{
    test: /\.js$/,
    use: "babel-loader",
    exclude: /node_modules/
},

然后需要我们在根目录下添加一个配置文件 .babelrc

这个根据需要进行配置

{
    "presets":[
        [
            "@babel/preset-env",
            {
                "targets":{
                    "chrome": "58",
                    "ie": "8",
                    "safari": "7"
                },
                "useBuiltIns":"usage",
                "corejs": "3"
            }
        ]
    ]
}

babel-loader:加载器

@babel/core:babel核心包,babel-loader的核心依赖

@babel/preset-env:ES语法分析包 , 只会转换语法, 如果进一步需要转换内置对象、实例方法,那就得用polyfill, useBuiltIns = useage 时,会参考目标浏览器(browserslist) 和 代码中所使用到的特性来按需加入 polyfill, 当然,使用 useBuiltIns = useage, 还需要填写另一个参数 corejs 的版本号

三、vue与webapck关联

想要webpack识别vue单文件组件 ,需要用到vue-loader 首先安装 vue-loader、css-loader、vue-style-loader 和 vue-template-compiler 需要在base.conf.js中添加rules

{
  test: /\.vue$/,
  loader: 'vue-loader'
},
{
  test: /\.css$/,
  use: ['vue-style-loader', 'css-loader']
}

我们还需要配置一个插件,然后还需要配置 resolve.alias 别名,不然 Webpack 没法找到 Vue 模块,然后在plugins数组中添加new VueLoaderPlugin(),还要配置别名,不然webpack无法找到vue模板 resolve: 这些选项能设置模块如何被解析

resolve: {
    extensions: ["*", ".js", ".json", ".vue"],
    alias: {
      vue$: "vue/dist/vue.esm.js",
      "@": path.resolve(__dirname, "../src"),
      "@src": path.resolve(__dirname, "../src/assets")
    }
}

安装vue 修改index.js文件

import Vue from 'vue';
import App from './App';

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
});

同目录下创建个App.vue文件

<template>
  <h1>Hello World!</h1>
</template>

<script>
  export default {
    name: 'App'
  }
</script>

<style> 
</style>

四、优化

优化CSS代码

这里我们使用 postcss 的 autoprefixer 插件为我们的 css 代码自动添加前缀以适应不同的浏览器。 修改module.rules的css配置文件

{
  test: /\.css$/,
  use: ['style-loader', 'css-loader', 'postcss-loader']
}
然后在我们项目的根目录下新增配置文件 postcss.config.js,内容如下:
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

开启热更新

参考Russ_Zhonglv-3的build.js

修改 webpack.dev.conf.js,在 devServer 属性中设置 hot 的值为 true,这就代表开启了热更新。但是只这样做还不够,需要我们添加一个插件,继续修改 webpack.dev.conf.js。 设置其 plugins 属性如下:

const webpack = require('webpack');
// 在文件头部引入 webpack 依赖
[
  new webpack.HotModuleReplacementPlugin()
]

这就开启了 css 热更新(因为 vue-style-loader 封装了 style-loader,热更新开箱即用),但是 JavaScript 热更新还不能用,每次修改代码我们都会刷新浏览器,所以我们需要继续配置。 为了使得 JavaScript 模块也能进行 HMR,我们需要在我们的 入口文件(index.js) 的底部添加如下代码:

if (module.hot) {
  module.hot.accept();
}

第三方库独立打包

引入hard-source-webpack-plugin,这个还不是太熟~

const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
  plugins: [
    new HardSourceWebpackPlugin(),
  ],

提取公共代码

使用 splitChucksPlugin 插件,这是 Webpack 自带的,不用安装第三方依赖。

使用方法: 在 webpack.base.conf.js 的 plugins 属性中添加如下插件对象;

new webpack.optimize.SplitChunksPlugin()

这代表你将使用默认的提取配置来提取你的公共代码,如果你不想使用默认配置,请给插件构造函数传入配置对象.

使用css预处理器

我这边使用的scss,需要安装node-sass, sass-loader, 添加rules到module

{
   test:/\.scss$/,
   use:['style-loader',
        {
            loader:'css-loader',
            options:{
                modules:true
            }
        },
         'sass-loader']
   },

css到单文件

npm install -D mini-css-extract-plugin 在base.conf.js配置中加入

var MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // 其它选项...
  module: {
    rules: [
      // ... 忽略其它规则
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV !== 'production'
            ? 'vue-style-loader'
            : MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // ... 忽略 vue-loader 插件
    new MiniCssExtractPlugin({
      filename: 'style.css'
    })
  ]
}

本人只是初尝试~ 如果文章存在任何问题,请直接指出 如果有什么想法~ 也可以直接留言哟