webpack 从零搭建一个 vue2 项目

2,483 阅读6分钟

本文针对的面试题:

  • 是否手动搭建过 vue 项目?
  • webpack 的基础配置了解?

最近身边好几位不同岗位的同事跳槽了,所以我也来复习(学习)一下项目搭建相关知识,让自己不要这么焦虑。

基于: vue2 & webpack5

来吧,手动搭建起来。

初始化项目

选择一个目录初始化项目

mkdir dashboard
cd dashboard
npm init

执行以上命令,根据提示初始化项目,在 dashboard 目录下会自动创建一个 package.json 文件。 像下面这样:

{
  "name": "dashbord",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "lucky",
  "license": "ISC"
}

引入 webpack

npm install webpack webpack-cli -D

执行以上命令会自动生成 node_module 目录及 package-lock.json 文件,并下载 webpack、webpack-cli 的包。 现在的目录结构是这样:

> node_module
package-lock.json
package.json

添加 gitignore 文件

在根目录下添加.gitignore 文件,设置 node_modules 文件夹不上传

node_modules/

webpack 基础配置

在根目录下新建 webapck 目录,创建 webpack.config.js 文件

touch webpack.config.js

webpack 的核心思想就是打包,将一切 js 以外的文件打包为 js 可以使用的文件。配置 webpack 的入口、出口、插件、模块

const path = require("path");
module.exports = {
  mode: "development", // 根据环境使用相应的内置优化
  entry: "/src/index.js", // 入口文件
  output: {
    // 出口文件
    filename: "bundle.js",
    path: path.resolve(__dirname, "../dist"),
    publicPath: "/",
  },
  plugins: [], // 使用的插件
  module: {
    generator: {}, // 生成器
    parser: {}, // 解析器
    rules: [], // 修改模块的创建方式
  }, // 模块加载方案
};

别忘了执行 npm i path

测试 webpack 打包

现在我们新建一个 index.js 文件

const a = { x: 1 };
console.log("测试打包!", a);

然后在命令行执行 webpack

webapck ./index.js

可以看到,在根目录下生成了一个 dist 目录,在 dist 目录下生成了我们指定的出口文件 bundle.js :

console.log("测试打包!", { x: 1 });

现在我们引入了 webpack 也能够正常打包了,下一步是配置文件打包到 html 中。

测试将代码打包至 html 文件中

先在根目录下生成一个 html 文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body></body>
</html>

要做到将 js、css 等代码打包到 html 文件中,我们需要用到 htmlWebpackPlugin。 html-webpack-plugin 的作用是:当使用 webpack 打包时,创建一个 html 文件,并把 webpack 打包后的静态文件自动插入到这个 html 文件当中。 在命令行执行以下命令,安装插件:

npm install html-webpack-plugin --save-dev

在 webpack.config.js 文件中引入以上插件:

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html",
      title: "Vue -> Web App",
      minify: {
        collapseWhitespace: true, // 去掉空格
        removeComments: true, // 去掉注释
      },
    }),
  ],
};

在命令行执行 webpack 命令:

webpack ./index.js --config ./webpack/webpack.config.js

现在根目录下自动生成了 dist 目录:

dist
 budle.js
 index.html

bundle.js:

/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
/******/ (() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = {
    /***/ "./index.js":
      /*!******************!*\
  !*** ./index.js ***!
  \******************/
      /***/ () => {
        eval(
          "const a = {x: 1};\nconsole.log('测试打包!', a);\n\n\n//# sourceURL=webpack:///./index.js?"
        );

        /***/
      },

    /******/
  };
  /************************************************************************/
  /******/
  /******/ // startup
  /******/ // Load entry module and return exports
  /******/ // This entry module can't be inlined because the eval devtool is used.
  /******/ var __webpack_exports__ = {};
  /******/ __webpack_modules__["./index.js"]();
  /******/
  /******/
})();

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Document</title>
    <!-- 注意这里 -->
    <script defer="defer" src="/bundle.js"></script>
  </head>

  <body></body>
</html>

观察以上代码,html 中自动插入了一个 script,其中引入了我们 index.js 打包后的代码。

到这里我们就完成了 webpack 打包的一个架子,但是日常开发我们还会使用 vue、react 等前端框架和 UI 框架,我们会在后面引入。

配置 npm run build

好的,为了后面测试方便,我们先在 package.json 中配置一个 build 自定义命令:

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

使用时直接 npm run build 就可以打包了。

引入 vue

  • 引入 vue vue 可以在 script 中引入,这里我们使用 npm 方式引入:
npm i vue

vue 是一个前端框架,它的两个核心思想就是:双向数据绑定、组件化。 在 src 目录下先新建一个 App.vue 文件

<template>
  <div>这是一个vue文件</div>
</template>

修改 html 文件

<body>
  <div id="app"></div>
</body>

修改 index.js:

import Vue from "vue";
import App from "./App.vue";
new Vue({
  el: "#app",
  render: (h) => h(App),
});

现在我们执行 npm run build 肯定是不行的,因为 webpack 还不能识别 vue 后缀的文件。需要先安装 vue-loader:

npm i vue-loader --save-dev
npm i cache-loader --save-dev
npm i thread-loader --save-dev

cache-loader 的作用: 在一些性能开销较大的 loader 之前添加 cache-loader,以便将结果缓存到磁盘里。

thread-loader 的作用:

把这个 loader 放置在其他 loader 之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行

修改 webpack.config.js:

const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
  plugins: [new VueLoaderPlugin()],
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: "cache-loader",
          },
          {
            loader: "thread-loader",
          },
          {
            loader: "vue-loader",
            options: {
              compilerOptions: {
                preserveWhitespace: false,
              },
            },
          },
        ],
        exclude: /node_modules/,
      },
    ],
  },
};

安装 vue-template-compiler 和 html-loader

npm i vue-template-compiler --save-dev
npm i html-loader --save-dev

现在执行 npm run build 打包后的文件用 node.js 静态服务跑起来可以看到我们 vue 组件中的 "这是一个 vue 文件" 字样。

配置 devServer

用 nodejs 来跑静态文件比较麻烦,现在我们配置 webapck 自带的开发工具 webpack devServer

npm install webpack-dev-server -D

在 webpack.config.js 中启用 devServer:

module.exports = {
  devServer: {
    port: 3005, // 启动端口
    hot: true, // 是否开启热模块替换
    open: true, // 是否开启自动打开页面
    proxy: {
      // 代理规则
      "/api": {
        target: "http://123.123.123.123:8080",
        secure: false,
        changeOrigin: true,
        pathRewrite: {
          // 重定向规则
          "^/api": "/api",
        },
      },
    },
  },
};

在 shell 中新增命令 dev:

    "dev": "webpack-dev-server --config ./webpack/webpack.config.js",

执行 npm run dev 项目在 http://localhost:3005 启动。注意如果端口占用了就换一个端口启动。 到这里我们就完成了一个 webpack 手动配置 vue 项目的基础框架了。接下来开始开发。这里我选择的是 vue 的好搭档 element-ui 来开发。

引入 element-ui

在命令行执行:

npm i element-ui -D

修改 index.js:

import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";

Vue.use(ElementUI);

这里我们引入了 element-ui 的 css 文件,所以要先添加 css-loader:

npm i style-loader css-loader -D
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

在 App.vue 中添加一个 el-button 测试一下:

<el-button type="primary">你好element-ui</el-button>

现在我们重启服务,可以看到页面上已经展示了这个蓝色(element-ui 默认的颜色)的按钮。开心~!

支持 scss

npm i css-loader style-loader sass sass-loader -D

在 webpack.config.js 文件中添加的 rules,

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

修改 ui 主题颜色

Element 的 theme-chalk 使用 SCSS 编写,如果你的项目也使用了 SCSS,那么可以直接在项目中改变 Element 的样式变量。新建一个样式文件,例如 element-variables.scss

/* 改变主题色变量 */
$--color-primary: teal;

/* 改变 icon 字体路径变量,必需 */
$--font-path: "~element-ui/lib/theme-chalk/fonts";

@import "~element-ui/packages/theme-chalk/src/index";

修改 index.js

// import "element-ui/lib/theme-chalk/index.css";
import "./element-variables.scss";

完成以后,界面上按钮的蓝色背景就切换成了绿茶色了。 后面我们就可以使用 element-ui 开始开发 crm 系统界面啦!

支持 ES6 语法

在app.vue中添加测试代码:

<script>
  export default {
    async created(){
      const timeout = function(time){
        return new Promise(resolve => {
          setTimeout(resolve, time);
        })
      };
      console.log('11111');
      await timeout(3000);
      console.log('22222');
    },
  }
</script>

保存以后,发现报错,原因是我们的webpack不支持ES6。 修改 webpack.config.js,添加 rules

      {
        test: /\.js$/,
        use: {
            loader: 'babel-loader',
            options: {
                cacheDirectory: true,
                presets: ['@babel/preset-env'],
                plugins: [
                    '@babel/plugin-transform-runtime',
                    '@babel/plugin-transform-modules-commonjs',
                  ],
            },
        },
        exclude: /node_modules/,
    },

配置 nginx

这个项目部署我采用的 nginx 代理的方式:

access_log /dev/stdout;

server {

  listen 80 default_server;

  location / {
    root /app/dist;
    try_files $uri $uri/ /index.html;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ( !-f $request_filename ) {
      rewrite ^/(.*) /index.html break;
    }
    if ($request_method = 'OPTIONS') {
      return 204;
    }
  }

  location /service-name/ {
    proxy_pass http://service-name:8080/;
    index index.html index.htm;
  }
}

根据 nginx 的文档,配置一个服务的正向代理没有什么问题,配置反向代理也很简单。不过这里我遇到一个区分不同环境的问题:不同的环境访问的后端的服务地址不同,需要动态配置。这里我采用的是直接配置成 ranchers 集群内的服务名称的方式。

开启 gzip 压缩

gzip 可以在 http 段加也可以在 server 段加。

http {
    gzip  on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
    gzip_disable "MSIE [1-6]\.";
    gzip_vary on;
}

总结

这篇文章包含的内容有:webpack 打包配置、识别 vue 文件、配置 devServer、element-ui、nginx 部署、gzip 配置。基础的框架已经有了,开发过程中再遇到需要的插件和loader再自行安装就好啦!

文章到这里就告一个段落了,以上步骤已经放到github上了,如果大家想看一个 CRM 系统开发框架搭建后续的内容,可以在评论区留言。

tips

以前新建 vue 项目都是 cli 工具,对搭建流程不太了解。 就在今年年初的时候,还一直很恐惧使用 webpack 自己搭建项目,随着最近 2 个多月的新项目搭建和部署上手,现在已经一点都不怕啦。 虽然还有很多不会的知识,但是,只要相信自己,并且勇敢踏出第一步,没有什么问题是不能解决的,大家一起加油呀!欢迎批评指正,喵!