面试官:自己搭建过vue开发环境吗?

640?wx_fmt=gif

开篇

前段时间,看到群里一些小伙伴面试的时候被面试官问到这类题目。平时大家开发vue项目的时候,相信大部分人都是使用 vue-cli脚手架生成的项目架构,然后 npm run install 安装依赖,npm run serve启动项目然后就开始写业务代码了。

但是对项目里的webpack封装和配置了解的不清楚,容易导致出问题不知如何解决,或者不会通过webpack去扩展新功能。

该篇文章主要是想告诉小伙伴们,如何一步一步的通过 webpack4来搭建自己的vue开发环境

首先我们要知道 vue-cli生成的项目,帮我们配置好了哪些功能?

  1. ES6代码转换成ES5代码

  2. scss/sass/less/styluscss

  3. .vue文件转换成js文件

  4. 使用 jpgpngfont等资源文件

  5. 自动添加css各浏览器产商的前缀

  6. 代码热更新

  7. 资源预加载

  8. 每次构建代码清除之前生成的代码

  9. 定义环境变量

  10. 区分开发环境打包跟生产环境打包

  11. ....

640?wx_fmt=other

1. 搭建 webpack 基本环境

该篇文章并不会细讲 webpack 是什么东西,如果还不是很清楚的话,可以先去看看 webpack官网

简单的说,webpack是一个模块打包机,可以分析你的项目依赖的模块以及一些浏览器不能直接运行的语言jsxvue等转换成 jscss文件等,供浏览器使用。

640?wx_fmt=other

1.1 初始化项目

在命令行中执行 npm init 然后一路回车就行了,主要是生成一些项目基本信息。最后会生成一个 package.json 文件

npm init

1.2 安装webpack

640?wx_fmt=other

1.3 写点小代码测试一下webpack是否安装成功了

新建一个src文件夹,然后再建一个main.js文件

console.log('hello webpack')

然后在 package.json 下面加一个脚本命令

640?wx_fmt=other

然后运行该命令

npm run serve

如果在 dist 目录下生成了一个mian.js文件,则表示webpack工作正常

2. 开始配置功能

  • 新建一个 build 文件夹,用来存放 webpack配置相关的文件

  • build文件夹下新建一个webpack.config.js,配置webpack的基本配置

  • 修改 webpack.config.js配置

640?wx_fmt=other
  • 修改package.json 文件,将之前添加的 serve 修改为

"serve": "webpack ./src/main.js --config ./build/webpack.config.js"

2.1 配置 ES6/7/8 转 ES5代码

  • 安装相关依赖

npm install babel-loader @babel/core @babel/preset-env
  • 修改webpack.config.js配置

640?wx_fmt=other
  • 在项目根目录添加一个 babel.config.js 文件

640?wx_fmt=other
  • 然后执行 npm run serve 命令,可以看到 ES6代码被转成了ES5代码了

2.1.1 ES6/7/8 Api 转es5

babel-loader只会将 ES6/7/8语法转换为ES5语法,但是对新api并不会转换。

我们可以通过 babel-polyfill 对一些不支持新语法的客户端提供新语法的实现

  • 安装

npm install @babel/polyfill
  • 修改webpack.config.js配置

在 entry 中添加 @babel-polyfill

640?wx_fmt=other

2.2 配置 scss 转 css

在没配置 css 相关的 loader 时,引入scsscss相关文件打包的话,会报错

  • 安装相关依赖

npm install sass-loader dart-sass css-loader style-loader -D

sass-loaderdart-sass主要是将 scss/sass 语法转为css

css-loader主要是解析 css 文件

style-loader 主要是将 css 解析到 html页面 的 style 上

  • 修改webpack.config.js配置

640?wx_fmt=other

2.3 配置 postcss 实现自动添加css3前缀

  • 安装相关依赖

npm install postcss-loader autoprefixer -D
  • 修改webpack.config.js配置

640?wx_fmt=other
  • 在项目根目录下新建一个 postcss.config.js

640?wx_fmt=other

2.3 使用 html-webpack-plugin来创建html页面

使用 html-webpack-plugin来创建html页面,并自动引入打包生成的js文件

  • 安装依赖

npm install html-webpack-plugin -D

  • 新建一个 public/index.html 页面

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

2.4 配置 devServer 热更新功能

通过代码的热更新功能,我们可以实现不刷新页面的情况下,更新我们的页面

  • 安装依赖

npm install webpack-dev-server -D

  • 修改webpack.config.js配置

通过配置 devServer 和 HotModuleReplacementPlugin 插件来实现热更新

640?wx_fmt=other

2.5 配置 webpack 打包 图片、媒体、字体等文件

  • 安装依赖

npm install file-loader url-loader -D

file-loader 解析文件url,并将文件复制到输出的目录中

url-loader 功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件复制到输出的目录中

  • 修改 webpack-config.js 配置 添加 rules 配置,分别对 图片,媒体,字体文件进行配置

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
module.exports = {
  
  module: {
    rules: [
      
      {
        test: /\.(jpe?g|png|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                    name: 'img/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'media/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'fonts/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
    ]
  },
  plugins: [
    
  ]
}

3. 让 webpack 识别 .vue 文件

  • 安装需要的依赖文件

npm install vue-loader vue-template-compiler cache-loader thread-loader -D
npm install vue -S

vue-loader 用于解析.vue文件

vue-template-compiler 用于编译模板

cache-loader 用于缓存loader编译的结果

thread-loader 使用 worker 池来运行loader,每个 worker 都是一个 node.js 进程。

  • 修改 webpack.config.js配置

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
  
  mode: 'development',
  entry: {
    
  },
  output: {
    
  },
  devServer: {
    
  },
  resolve: {
    alias: {
      vue$: 'vue/dist/vue.runtime.esm.js'
    },
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'cache-loader'
          },
          {
            loader: 'thread-loader'
          },
          {
            loader: 'vue-loader',
            options: {
              compilerOptions: {
                preserveWhitespace: false
              },
            }
          }
        ]
      },
      {
        test: /\.jsx?$/,
        use: [
          {
            loader: 'cache-loader'
          },
          {
            loader: 'thread-loader'
          },
          {
            loader: 'babel-loader'
          }
        ]
      },
      
    ]
  },
  plugins: [
    
    new VueLoaderPlugin()
  ]
}
  • 测试一下

  1. 在 src 新建一个 App.vue

// src/App.vue
<template>
  <div class="App">
    Hello World
  </div>
</template>

<script>
export default {
  name: 'App',

  data() {
    return {};
  }
};
</script>

<style lang="scss" scoped>
@import './assets/styles/var.scss';
.App {
  color: $primary-color;
}
</style>
  1. 修改 main.js

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

new Vue({
  render: h => h(App)
}).$mount('#app')
  1. 运行一下

npm run serve

4. 定义环境变量

通过 webpack提供的DefinePlugin插件,可以很方便的定义环境变量

plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        VUE_APP_BASE_URL: JSON.stringify('http://localhost:3000')
      }
    }),
]

5. 区分生产环境和开发环境

新建两个文件

  • webpack.dev.js 开发环境使用

  • webpack.prod.js 生产环境使用

  • webpack.config.js 公用配置

  • 开发环境与生产环境的不同

5.1 开发环境

  1. 不需要压缩代码

  2. 需要热更新

  3. css不需要提取到css文件

  4. sourceMap

  5. ...

5.2 生产环境

  1. 压缩代码

  2. 不需要热更新

  3. 提取css,压缩css文件

  4. sourceMap

  5. 构建前清除上一次构建的内容

  6. ...

  • 安装所需依赖

npm i @intervolga/optimize-cssnano-plugin mini-css-extract-plugin clean-webpack-plugin webpack-merge copy-webpack-plugin -D
  1. @intervolga/optimize-cssnano-plugin 用于压缩css代码

  2. mini-css-extract-plugin 用于提取css到文件中

  3. clean-webpack-plugin 用于删除上次构建的文件

  4. webpack-merge 合并 webpack配置

  5. copy-webpack-plugin 用户拷贝静态资源

5.3 开发环境配置

  • build/webpack.dev.js


const merge = require('webpack-merge')
const webpackConfig = require('./webpack.config')
const webpack = require('webpack')
module.exports = merge(webpackConfig, {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  module: {
    rules: [
      {
        test: /\.(scss|sass)$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2
            }
          },
          {
            loader: 'sass-loader',
            options: {
              implementation: require('dart-sass')
            }
          },
          {
            loader: 'postcss-loader'
          }
        ]
      },
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('development')
      }
    }),
  ]
})
  • webpack.config.js


const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
  entry: {
    
    main: path.resolve(__dirname, '../src/main.js')
  },
  output: {
    
    path: path.resolve(__dirname, '../dist'),
    
    filename: 'js/[name].[hash:8].js',
    
    chunkFilename: 'js/[name].[hash:8].js',
    
    publicPath: '/'
  },
  devServer: {
    hot: true,
    port: 3000,
    contentBase: './dist'
  },
  resolve: {
    alias: {
      vue$: 'vue/dist/vue.runtime.esm.js'
    },
    extensions: [
      '.js',
      '.vue'
    ]
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'cache-loader'
          },
          {
            loader: 'vue-loader',
            options: {
              compilerOptions: {
                preserveWhitespace: false
              },
            }
          }
        ]
      },
      {
        test: /\.jsx?$/,
        loader: 'babel-loader'
      },

      {
        test: /\.(jpe?g|png|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'img/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'media/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 4096,
              fallback: {
                loader: 'file-loader',
                options: {
                  name: 'fonts/[name].[hash:8].[ext]'
                }
              }
            }
          }
        ]
      },
    ]
  },
  plugins: [
    new VueLoaderPlugin(),

    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html')
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ]
}

5.4 生产环境配置

const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssnanoPlugin = require('@intervolga/optimize-cssnano-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = merge(webpackConfig, {
  mode: 'production',
  devtool: '#source-map',
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\\/]node_modules[\\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    }
  },
  module: {
    rules: [
      {
        test: /\.(scss|sass)$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2
            }
          },
          {
            loader: 'sass-loader',
            options: {
              implementation: require('dart-sass')
            }
          },
          {
            loader: 'postcss-loader'
          }
        ]
      },
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: 'production'
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
      chunkFilename: 'css/[name].[contenthash:8].css'
    }),
    new OptimizeCssnanoPlugin({
      sourceMap: true,
      cssnanoOptions: {
        preset: [
          'default',
          {
            mergeLonghand: false,
            cssDeclarationSorter: false
          }
        ]
      }
    }),
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../public'),
        to: path.resolve(__dirname, '../dist')
      }
    ]),
    new CleanWebpackPlugin()
  ]
})

5.5 修改package.json

"scripts": {
    "serve": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
},

6 总结

到目前为止,我们已经成功的自己搭建了一个 vue 开发环境,不过还是有一些功能欠缺的,有兴趣的小伙伴可以交流交流。在搭建过程中,还是会踩很多坑的。

如果还不熟悉 webpack 的话,建议自己搭建一次。可以让自己能深入的理解 vue-cli 替我们做了什么

640?wx_fmt=other

 热 文 推 荐 

☞ 浏览器中的垃圾回收与内存泄漏

☞ Web 组件势必取代前端?

☞ 作为一个开发者,我创业了

☞ 如何优雅处理前端的异常?

☞ 从0到1完成一个Babel插件

640?wx_fmt=png

640?wx_fmt=png

你也“在看”吗?

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值