blog icon indicating copy to clipboard operation
blog copied to clipboard

自己搭建一个Vue项目工程

Open wuxianqiang opened this issue 4 years ago • 0 comments

这里的意思是不在使用Vue-cli脚手架生成项目了,而是自己从零开始配置webpack打包,完成一个完整的项目。

首先说一下为什么不在使用脚手架,首先是脚手架一般不适合在公司作为项目开发的主力军,脚手架配置并不符合公司的业务场景,一般还要拓展一些额外的功能,在现有的脚手架中修改变得额外的麻烦,所以一般公司都是开发人员自己配置项目打包。

现在我们开始吧!

配置ES6

为了支持ES6,你需要安装如下包

babel-loader 在webpack中必须要loader才能处理代码,所以要安装loader
@babel/core 这个是babel里面自己的方法
@babel/preset-env 这里是将现有已经支持的高级语法转换成低级语法
@babel/runtime-corejs3 这个是浏览器还没有的语法,也就是提案中的语法,浏览器没有,就自己实现,这个包就是实现的所有提案代码
@babel/runtime 如果每个代码都使用了提案的代码,那么每个代码都有一个实现的方法,这样就重复了,那么就要使用把他们放到一个单独的文件中,作为模块带引入
@babel/plugin-transform-runtime 具体怎么引入,需要生成一段代码import这样的添加到我们的代码中,之前是没有的,要用来生成插入到我们的代码中
@babel/plugin-proposal-class-properties 这次插件是支持一些类里面的写法,可以选择性的安装

我们开始安装把,主要是开发依赖还是生成依赖

npm i @babel/core @babel/plugin-transform-runtime @babel/preset-env babel-loader @babel/plugin-proposal-class-properties -D
npm i @babel/runtime @babel/runtime-corejs3 -S

然后创建.babelrc 文件

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ],
    "@babel/plugin-proposal-class-properties"
  ]
}

我们在开始创建我们的文件

build
   webpack.base.conf.js
   webpack.dev.conf.js
   webpack.dll.conf.js
   webpack.pro.conf.js
src
    main.js
    App.vue
template.html

我们的webpack配置都写在build文件夹中,现在在webpack.base.conf.js添加代码,这是公共的webpack代码配置

const path = require('path')
const webpack = require('webpack')
const dir = process.cwd()

module.exports = {
 entry: {
   app: path.resolve(dir, 'src/main.js')
 },
 resolve: {
   extensions: [
     '.js', '.vue', '.less', '.css'  // 这个是配置node查找文件的顺序
   ],
   alias: {
     'src': path.resolve(dir, 'src')  // 这个是配置路径别名的
   }
 },
 module: {
   rules: [
     {
       test: /\.js$/,
       include: dir,
       exclude: /node_modules/,
       loader: 'babel-loader'
     },
   ]
 }
}

现在在webpack.dev.conf.js添加代码,这是开发环境的代码位置

const webpack = require('webpack')
const path = require('path')
const Merge = require('webpack-merge')
const dir = process.cwd()
const baseConfig = require('./webpack.base.conf')

module.exports = Merge(baseConfig, {
 mode: 'development',
 output: {
   filename: '[name].js',
   path: path.resolve(dir, 'dist')
 },
 devtool: 'source-map'
})

配置Vue

配置Vue我们需要安装 vue-template-compiler 模板编译的包,还有loader处理

npm i vue-loader vue-template-compiler -D

然后再在基础的webpack配置中添加规则,一定要记住添加把 VueLoaderPlugin 添加到插件中

const path = require('path')
const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const dir = process.cwd()

module.exports = {
  entry: {
    app: path.resolve(dir, 'src/main.js')
  },
  resolve: {
    extensions: [
      '.js', '.vue', '.less', '.css'
    ],
    alias: {
      'src': path.resolve(dir, 'src')
    }
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        include: dir,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

加载样式

样式的话我们使用less,我们需要安装三个loader

npm i less less-loader style-loader css-loader  -D

webpack会调用less-loader处理less文件,而处理less文件需要less来编译成CSS css-loader 处理 @import 和 url() 转换成 import/require() 打包的时候才可以处理文件之间的依赖关系 style-loader 创建一个style把样式插入到HTML中。

修改我们的开发环境的webpack配置添加规则

      {
        test: /\.(css|less)$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'less-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }

加载图片

因为像 .png 这样的文件不是一个 JavaScript 模块,你需要配置 webpack 使用 file-loader 或者 url-loader 去合理地处理它们。

转换资源 URL 的好处是:

file-loader 可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存。此外,这意味着 你可以就近管理图片文件,可以使用相对路径而不用担心部署时 URL 的问题。使用正确的配置,webpack 将会在打包输出中自动重写文件路径为正确的 URL。

url-loader 允许你有条件地将文件转换为内联的 base-64 URL (当文件小于给定的阈值),这会减少小文件的 HTTP 请求数。如果文件大于该阈值,会自动的交给 file-loader 处理。

安装依赖

npm i file-loader url-loader  -D

在开发环境的webpack配置中添加对应的规则

      {
        test: /\.(png|svg|jpe?g)$/i,
        loader: 'url-loader',
        options: {
          esModule: false
        }
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        loader: 'file-loader',
        options: {
          esModule: false
        }
      },

动态连接库

我们通常需多次打包Vue全家桶,但是我们每次打包都费时费力,所以可以提前进行打包处理,那就要使用到动态连接库

// webpack.dill.js
const webpack = require('webpack')
const path = require('path')
const dir = process.cwd()

module.exports = {
  mode: 'production',
  entry: {
    vendor: ['vue', 'vuex', 'vue-router']
  },
  output: {
    library:  'familybucket',
    filename: '[name].dll.js'
  },
  plugins: [
    new webpack.DllPlugin({
      name: 'familybucket',
      path: path.resolve(dir, 'manifest.json')
    })
  ]
}

打包完成之后会生成vendor.dll.js文件,这个文件需要在HTML模板中引用,还有manifest.json文件,这个文件需要在webpack中引用,我们需要修改基础的webpack配置

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const dir = process.cwd()

module.exports = {
  entry: {
    app: path.resolve(dir, 'src/main.js')
  },
  resolve: {
    extensions: [
      '.js', '.vue', '.less', '.css'
    ],
    alias: {
      'src': path.resolve(dir, 'src')
    }
  },
  module: {
    rules: [
      {
        test: /\.(jsx?|babel|es6)$/,
        include: dir,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(dir, 'template.html'),
      chunks: ['app']
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(dir, 'manifest.json')
    }),
    new AddAssetHtmlPlugin({ filepath: path.resolve(dir, 'dist/vendor.dll.js') }),
    new ProgressBarPlugin(),
    new VueLoaderPlugin()
  ]
}

我们需要安装依赖

npm i add-asset-html-webpack-plugin html-webpack-plugin progress-bar-webpack-plugin -D

我们通过add-asset-html-webpack-plugin这个插件可以在HTML模板中添加任何我们想要的静态资源 html-webpack-plugin是把HTML模板中添加我们打包后的JS文件 progress-bar-webpack-plugin 这个是打包使用使用的进度条方便我们查看

代码校验

我们需要使用的依赖有

npm i eslint-loader eslint  eslint-friendly-formatter -D

在开发的webpack中添加对应的规则

      {
        enforce: 'pre',
        test: /\.vue$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
        include: path.resolve(dir, 'src'),
        options: {
          formatter: require('eslint-friendly-formatter'),
          cache: true,
          emitWarning: true,
          emitError: true
        }
      },

关于eslint的使用是通过生成配置文件的,我们需要使用命令eslint --init,通过问答的形式,最后生成了大概如下.eslintrc.js

module.exports = {
    "env": {
        "node": true,
        "es6": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:vue/essential"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    },
    "parserOptions": {
        "ecmaVersion": 2020,
        "sourceType": "module"
    },
    "plugins": [
        "vue"
    ],
    "rules": {
        "indent": ["error", 2],
        "space-before-function-paren": ["error", "always"],
        "quotes": ["error", 'single']
    }
};

生成了这个文件,编辑器才有代码校验,会进行报错,通常我们如果报错,页面要直接显示出来,要配置webpack-dev-server插件中的配置选项

  devServer: {
    host: '0.0.0.0',
    port: 8000,
    hot: true,
    open: true,
    overlay: {
      errors: true, // 页面显示报错信息
      warnings: true // 页面显示报错信息
    },
    contentBase: path.resolve(dir, 'dist')
  },

CSS前缀

需要安装

npm i postcss-loader postcss-preset-env cssnano mini-css-extract-plugin -D

这里说明一下,postcss-loader会调用postcss-preset-env来添加前缀,负责添加前缀的是postcss-preset-env,而 cssnano 这个包是负责删除无用的CSS,还有mini-css-extract-plugin是用来分离css成单独的文件的。下面是生成环境的配置。

const webpack = require('webpack')
const path = require('path')
const Merge = require('webpack-merge')
const dir = process.cwd()
const baseConfig = require('./webpack.base.conf')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = Merge(baseConfig, {
  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin(),
      new UglifyJsPlugin()
    ]
  },
  mode: 'production',
  output: {
    filename: 'js/[name]_[hash].js',
    path: path.resolve(dir, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|svg|jpe?g)$/,
        loader: 'url-loader',
        options: {
          limit: 8192,
          name: 'img/[name].[hash:7].[ext]',
          publicPath: '/',
          esModule: false
        }
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[hash:7].[ext]',
          esModule: false
        }
      },
      {
        test: /\.(css|less)$/,
        loaders: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              insertAt: 'top'
            }
          },
          {
            loader: 'css-loader',
            options: { importLoaders: 2 }
          },
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  },
  devtool: 'cheap-source-map',
  plugins: [
    new webpack.DefinePlugin({
      NODE_ENV: JSON.stringify('production')
    }),
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ['**/*', '!vendor.dll.js']
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[hash:7].css'
    })
  ]
})

上面还有一下没有提到的插件,包括压缩css和压缩js的功能,需要安装

npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D

记住新版本的webpack是写法不一样,要写到optimization.minimizer里面

  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin(),
      new UglifyJsPlugin()
    ]
  },

清除之前

我们往往需要清除之前的打包文件,需要安装依赖是

npm i clean-webpack-plugin -D

用法是

    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ['**/*', '!vendor.dll.js']
    }),

在这里说明一下,配置cleanOnceBeforeBuildPatterns传个数组过来,!非号表示不要清除的,*星号表示所有的都要清除

wuxianqiang avatar Feb 28 '20 07:02 wuxianqiang