使用 Vue2.x + webpack4.x 从零开始一步步搭建项目框架

0. 前言
首先不得不说 vue-cli + vue-webpack 模版真的很方便,vue init webpack my-vue-project 就搭好框架了,而且开发环境生产环境都有了。npm run dev 启动开发环境,npm run build 发布生产环境。几个命令全部搞定了。但是模版有时候可能不够灵活,或者我们想修改其中一些东西,面对这些需求的时候,理解怎么搭建起这个 vue + webpack 的环境还是很有用的。那最快捷的方式,当然是自己从头开始搭一遍啦。

1. 新建项目

因为不用 vue-cli 和 vue 模版,所以一开始我们就建个空文件夹。

mkdir my-vue-project
cd my-vue-project
npm init # 初始化项目。这时候会问你一些问题,比如项目名称、作者之类的,照着提示回答问题就好

 2. 安装基本的 npm 包
首先肯定要安装 vue 和 webpack。然后 webpack 4.x已经把 cli 单独拎出来了,所以还要安装 webpack-cli;然后因为 webpack 本身其实直接能处理的只有 js 资源,是通过各种 loader 让其他资源可以被 webpack 打包处理的。那么现在我们要用 vue 写单文件组件(就是 .vue 文件),所以就还要安装 vue-loader。

npm install vue vue-loader webpack webpack-cli --save-dev # --save-dev表示这些只是开发环境所需的依赖

3. 添加项目各种入口文件

入口文件都是很普遍的那种,比如根目录下的 index.htmlsrc 文件夹下的 main.jsapp.vue 等等,我就不一一解释了。

添加入口文件后的项目目录如下:

.
├── dist/
|	├── ...
├── src/
|	├── main.js
|   ├── app.vue
├── node_modules/
|	├── ...
├── index.html
├── package.json

4. 添加 webpack 配置文件

在项目根目录下添加 webpack.config.js 文件。内容如下:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    entry: path.join(__dirname, 'src/main.js'), // 项目总入口js文件
    // 输出文件
    output: {
        path: path.join(__dirname, 'dist'), // 所有的文件都输出到dist/目录下
        filename: 'bundle.js'
    },
    module: {
        rules: [{
                // 使用vue-loader解析.vue文件
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/,
                // 要加上style-loader才能正确解析.vue文件里的<style>标签内容
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin() // 最新版的vue-loader需要配置插件
    ]
};

5. 添加构建脚本

 package.json 文件的 scripts 属性里添加 build 脚本

   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "webpack --config webpack.config.js"
   },

因为我们添加了一些引用,比如 style-loadercss-loader 等,所以也要安装相应的包。

npm i style-loader css-loader vue-template-compiler --save-dev

 安装好依赖后运行 npm run build 来构建项目  或者可以继续往下。

6. 添加图片、CSS 预处理器等 loader
图片 loader 用的是 url-loader,它依赖于 file-loader,比 file-loader 多了一个可以比小于一定大小的图片直接转化成 base64 的形式插入到 html 页面,可以减少网络请求。

CSS 预处理器 我选的是 SASS。

安装依赖:

npm i file-loader url-loader node-sass sass-loader --save-dev

 修改 webpack.config.js

 

		module.exports = 
			{
                 test: /\.css$/,
                 // 要加上style-loader才能正确解析.vue文件里的<style>标签内容
                 use: ['style-loader', 'css-loader']
            },
            {
                test: /\.scss$/,
                use: [
                    // 处理顺序是从sass-loader到style-loader
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        // 当文件大小小于limit byte时会把图片转换为base64编码的dataurl,否则返回普通的图片
                        limit: 8192,
                        name: 'dist/assest/images/[name]-[hash:5].[ext]' // 图片文件名称加上内容哈希
                    }
                }]
             }
         ]
     },
     ...

7. 添加 postcss-loader + autoprefixer,自动添加 css 浏览器前缀

安装依赖:

npm i postcss-loader autoprefixer --save-dev

 新增 postcss 配置文件 postcss.config.js

const autoprefixer = require('autoprefixer');

module.exports = {
    plugins: [
        autoprefixer({
            browsers: ['last 5 versions']
        })
    ]
};

修改 webpack.config.js

	...
	module: {
        rules: [
			...
			{
                test: /\.css$/,
                use: [
                    // 要加上style-loader才能正确解析.vue文件里的<style>标签内容
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }
                    },
                    'postcss-loader'
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    // 处理顺序是从sass-loader到style-loader
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            sourceMap: true
                        }
                    },
                    'sass-loader'
                ]
            },
            ...

8. 添加 babel-loader,转译 es6 代码为 es5 代码

添加.babelrc文件

{
    "presets": [
        "env"
    ],
    "plugins": [
        "transform-vue-jsx"
    ]
}
安装依赖:npm i babel-loader@7 babel-core babel-preset-env --save-dev
修改 webpack.config.js,module.rules 再加一条:
{
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/, // 不处理这两个文件夹里的内容
    loader: 'babel-loader'
}

9. 添加 html-webpack-plugin,自动生成 index.html 的内容
添加 HtmlWebpackPlugin,在 dist 文件夹也生成 index.html 页面,而且会自动加上对项目入口 js 文件的引用,也就是说我们自己写的 index.html 可以不用再手动指定 js 文件了,它会把 webpack 配置里的 entry 当中指定的 js 都插入到生成的 index.html 中,而且当输出的文件添加上 hash 值时也可以自动跟踪。

安装依赖

npm i html-webpack-plugin --save-dev

继续修改
 const path = require('path');
 const VueLoaderPlugin = require('vue-loader/lib/plugin');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
  
 module.exports = {
     entry: path.join(__dirname, 'src/main.js'), // 项目总入口js文件
     // 输出文件
     output: {
         path: path.join(__dirname, 'dist'),
-        filename: 'bundle.js'
+        filename: 'bundle-[hash].js' // 输出文件的名称加上hash值
     },
     module: {
         rules: [{
             ...
         ]
     },
     plugins: [
-        new VueLoaderPlugin() // 最新版的vue-loader需要配置插件
+        new VueLoaderPlugin(), // 最新版的vue-loader需要配置插件
+        new HtmlWebpackPlugin({
+            filename: 'index.html', // 生成的文件名称
+            template: 'index.html', // 指定用index.html做模版
+            inject: 'body' // 指定插入的<script>标签在body底部
+        })
     ]
 };

10. 添加 clean-webpack-plugin,每次 build 之前可以自动先清除输出文件夹
如果我们对输出的文件名称加上了哈希值的话,每次修改之后的哈希值都不一样,就是每次都生成了一个新的文件,那旧的那些其实就是多余的文件了(上面那张图的 dist 文件夹可以看出来)。因此我们可以用 clean-webpack-plugin 这个插件,在每次 build 之前先清除输出文件夹。

安装依赖

npm i clean-webpack-plugin --save-dev

修改 webpack.config.js,plugins 数组添加一项

 const path = require('path');
 const VueLoaderPlugin = require('vue-loader/lib/plugin');
 const HtmlWebpackPlugin = require('html-webpack-plugin');
+const CleanWebpackPlugin = require('clean-webpack-plugin');
  
 module.exports = {
     ...
     plugins: [
        new VueLoaderPlugin(), // 最新版的vue-loader需要配置插件
        new HtmlWebpackPlugin({
            filename: 'index.html', // 生成的文件名称
            template: 'index.html', // 指定用index.html做模版
            inject: 'body' // 指定插入的<script>标签在body底部
        }),
+        new CleanWebpackPlugin(['dist'])
    ]
 };

11. 添加 webpack-dev-servercross-env 配置更友好的开发环境
webpack-dev-server 可以在本地启动一个服务器,而且当有任何文件修改的时候会自动重新打包,并且刷新浏览器页面。此外 devServer 还有很多其他配置项,让我们可以更方便的开发。

安装依赖

npm i webpack-dev-server cross-env --save-dev

用上 cross-env 是因为我们接下来要修改 package.json 里的 scripts,而不同的平台写scripts 的方式不一样。

修改 package.json 里的 scripts

    ...
     "scripts": {
         "test": "echo \"Error: no test specified\" && exit 1",
-        "build": "webpack --config webpack.config.js"
+        "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
+        "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
     },
   ....

12. 配置生产环境 css 单独分离打包,方便浏览器缓存+单独打包类库文件

因为类库文件是不用像业务代码一样经常更新的,单独打包可以让它们在浏览器里缓存,提高加载速度。

安装依赖

npm i mini-css-extract-plugin --save-dev

webpack.config.js 完整版本:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const Webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const devServerPort = 8000; // 开发服务器端口号

// 是否是开发环境
// process.env拿到的是一个对象,它的属性可以通过命令行参数传入
// 这个NODE_ENV就是从package.json的dev/build scripts传进来的
const isDev = process.env.NODE_ENV === 'development';

const config = {
  // 指定webpack的模式,然后一些第三方库(比如vue、react等)也会针对这个值采用不同的包
  // 像vue就有完整版、运行时版等。参考:https://vuejs.org/v2/guide/deployment.html#With-Build-Tools
  mode: isDev ? "development" : "production",
  entry: path.join(__dirname, 'src/main.js'), // 项目总入口js文件
  // 输出文件
  output: {
    path: path.join(__dirname, 'dist'),
    /**
     * hash跟chunkhash的区别,如果entry有多个,或者需要单独打包类库到
     * 一个js文件的时候,hash是所有打包出来的每个js文件都是同样的哈希,
     * 而chunkhash就是只是那个chunk的哈希,也就是chunkhash如果那个chunk
     * 没有变化就不会变,而hash只要某一个文件内容有变化都是不一样的,所以
     * 用chunkhash区分开每一个文件有变化时才更新,让浏览器起到缓存的作用
     */
    filename: isDev ? 'bundle.[hash:8].js' : '[name].[chunkhash:8].js',
  },
  module: {
    rules: [{
      // 使用vue-loader解析.vue文件
      test: /\.vue$/,
      loader: 'vue-loader'
    },
    {
      test: /\.(gif|jpg|jpeg|png|svg)$/i,
      use: [{
        loader: 'url-loader',
        options: {
          // 当文件大小小于limit byte时会把图片转换为base64编码的dataurl,否则返回普通的图片
          limit: 8192,
          name: 'dist/assest/images/[name]-[hash:5].[ext]' // 图片文件名称加上内容哈希
        }
      }]
    },
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/, // 不处理这两个文件夹里的内容
      loader: 'babel-loader'
    }
    ]
  },
  plugins: [
    new VueLoaderPlugin(), // 最新版的vue-loader需要配置插件
    new HtmlWebpackPlugin({
      filename: 'index.html', // 生成的文件名称
      template: 'index.html', // 指定用index.html做模版
      inject: 'body' // 指定插入的<script>标签在body底部
    }),
    new CleanWebpackPlugin(['dist'])
  ],
  /**
   * 添加可以自动解析的扩展
   * 就是 import 的时候可以不用写后缀也能正确引用文件了
   * eg:添加了'.vue',import App from './app.vue' 可以写成 import App from './app' 了
   * 参考:https://webpack.docschina.org/configuration/resolve/#resolve-extensions
   */
  resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.json', '.vue']
  }
};

// 如果是开发环境
if (isDev) {
  config.module.rules.push({
    test: /\.css$/,
    use: [
      // 要加上style-loader才能正确解析.vue文件里的<style>标签内容
      'style-loader',
      {
        loader: 'css-loader',
        options: {
          importLoaders: 1
        }
      },
      'postcss-loader'
    ]
  }, {
    test: /\.scss$/,
    use: [
      // 处理顺序是从sass-loader到style-loader
      'style-loader',
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          sourceMap: true
        }
      },
      'sass-loader'
    ]
  });

  // 指定开发环境启动的服务器的信息
  config.devServer = {
    // contentBase: path.join(__dirname, 'dist'),
    port: devServerPort,
    host: '0.0.0.0', // 配置成0.0.0.0的话通过ip,localhost都能访问
    overlay: {
      errors: true // 如果有编译错误的话直接显示到页面上
    },
    hot: true // 开启模块热替换【https://webpack.docschina.org/guides/hot-module-replacement】
  };
  config.plugins.push(
    new Webpack.HotModuleReplacementPlugin() // 模块热替换插件
  );

  // 生成source map,方便调试
  // https://webpack.docschina.org/configuration/devtool/#src/components/Sidebar/Sidebar.jsx
  config.devtool = 'cheap-eval-source-map';
} else { // 生产环境

  // 把css分离打包到单独的文件
  config.module.rules.push({
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      {
        loader: 'css-loader',
        options: {
          importLoaders: 1
        }
      },
      'postcss-loader'
    ]
  }, {
    test: /\.scss$/,
    use: [
      MiniCssExtractPlugin.loader,
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          sourceMap: true
        }
      },
      'sass-loader'
    ]
  });
  config.plugins.push(
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "style.[hash:8].css",
      chunkFilename: "[id].[hash:8].css"
    })
  );

  // 单独打包第三方类库文件(比如vue框架)
  config.optimization = {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    },
    runtimeChunk: {
      name: entrypoint => `runtimechunk~${entrypoint.name}`
    }
  };
}

module.exports = config;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值