Project introduction

This project uses VUE as the front-end framework, thinkJs as the back-end framework, to build a personal blog website, the page is divided into blog display and background management, the main purpose is to learn to use thinkJs. Now I have only finished the main blog adding, deleting and changing functions. I found that the configuration of Webpack encountered some pits. Here I will record them first. The project directory structure is as follows:

Project github address click here

Project introduction

Setup Process After nodeJS is installed, thinkJS is installed first

npm install -g think-cli;
thinkjs new self_blog;
npm install;
npm start;
Copy the code

With the initial thinkJs project setup complete, the vue project is created. Create a new folder system under the project root directory.

npm install -g vue-cli
vue init webpack self_blog
cd self_blog
npm install
npm run dev
Copy the code

Webpack configuration

The front page of the project is divided into blog display and background management. The idea here is to put the background management page under sytem SRC, create a blog folder under Sytem, and put the foreground display page, generate two HTML when packaging, and realize webpack multi-page application. The directory is as follows:

Webpack. Base. Conf. Js configuration

Use vue-CLI to create the project and NPM run dev to run the development environment. Webpack-dev-server will be used as the front-end server to achieve hot loading. The packaged file will only be in memory, so the idea here is to generate it directly into the View directory of the thinkJs project and route it back to the front end via the server side. In this way, you only need to start the services on the back end, because the files are already packaged on the server side and the front end does not have cross-domain access to the back end. So there is no need to configure devServer, but to change the generation directory of static resources such as HTML and JS. Let’s look at the code for webpack.base.conf.js:

"use strict";
const path = require("path");
const utils = require("./utils");
const config = require(".. /config");
const vueLoaderConfig = require("./vue-loader.conf");
const webpack = require("webpack");
const baseFileName = require(".. /package.json").name;
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const cleanWebpackPlugin = require("clean-webpack-plugin");
const AssetsPlugin = require("assets-webpack-plugin");
function resolve(dir) {
return path.join(__dirname, "..", dir);
}

module.exports = {
context: path.resolve(__dirname, ".. /"),
entry: {
  app: "./src/main.js".blog: "./blog/index.js"
},
output: {
  path: config.build.assetsRoot,
  filename: "[name].js".publicPath:
    process.env.NODE_ENV === "production"
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
},
resolve: {
  extensions: [".js".".vue".".json"].alias: {
    vue$: "vue/dist/vue.esm.js"."@": resolve("src")}},externals: {
  vue: "Vue"."vue-router": "VueRouter".echarts: "echarts".ElementUI: "element-ui"
},
module: {
  rules: [{test: /\.vue$/.loader: "vue-loader".options: vueLoaderConfig
    },
    {
      test: /\.js$/.loader: "babel-loader".include: [
        resolve("src"),
        resolve("test"),
        resolve("node_modules/webpack-dev-server/client")]}, {test: /\.(png|jpe? g|gif|svg)(\? . *)? $/.loader: "url-loader".exclude: [resolve("src/icons")].options: {
        limit: 10000.name: utils.assetsPath(baseFileName + "/img/[name].[hash:7].[ext]")}}, {test: /\.svg$/.loader: "svg-sprite-loader".include: [resolve("src")].options: {
        symbolId: "icon-[name]"}}, {test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/.loader: "url-loader".options: {
        limit: 10000.name: utils.assetsPath(baseFileName + "/media/[name].[hash:7].[ext]")}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/.loader: "url-loader".options: {
        limit: 10000.name: utils.assetsPath(baseFileName + "/fonts/[name].[hash:7].[ext]"}}]},plugins: [
  new AssetsPlugin({
    filename: "build/webpack.assets.js".processOutput: function(assets) {
      return "window.WEBPACK_ASSETS=" + JSON.stringify(assets); }}),new webpack.optimize.CommonsChunkPlugin({
    name: "vendor".minChunks: function(module) {
      return (
        module.resource &&
        /\.js$/.test(module.resource) &&
        module.resource.indexOf(path.join(__dirname, ".. /node_modules"= = =))0); }}),new webpack.optimize.CommonsChunkPlugin({
    name: "manifest".minChunks: Infinity
  }),
  new ExtractTextPlugin({
    filename: utils.assetsPath(
      baseFileName + "/css/[name].[contenthash].css"
    ),
    allChunks: true
  }),
  // Delete the static resources generated by the previous compilation before compilation
  new cleanWebpackPlugin(["www/static/self_blog"."view/blog"] and {root: resolve(".. /")})],node: {
  setImmediate: false.dgram: "empty".fs: "empty".net: "empty".tls: "empty".child_process: "empty"}};Copy the code

The first thing that needs to be changed is the entry file. Because it is a multi-page application, multiple entry files are required to ensure that they are packaged into different chunks. We know that normal vUE – CLI-created but page-based applications are packaged into three chunks: Vendor.js (third-party dependencies), manifest.js (asynchronous loading implementation), and app.js (business code). A new entry file is added here. When packaged, a chunk named blog.js is generated, containing the JS business code for the foreground display page.

The clean-webpack-plugin is used to delete static js, CSS and other resources generated after each compilation. Otherwise, these resources will always be stored in the directory you generate because they are not the same name.

In addition to some third party dependencies, vue,elementUI and so on, I used CDN to import, so that when packaging these dependencies will not enter, thus reducing the size of Vendor.js. This is done by configuring externals to define these dependencies, and then importing javascript directly into the CDN using script tags in the HTML generated template.

Note that if you want to use vue-devtool for development, you need to include vue.js, and if not, vue.min.js.

Webpack. Dev. Conf. Js configuration

Take a look at the code:

"use strict";
const utils = require("./utils");
const webpack = require("webpack");
const config = require(".. /config");
const merge = require("webpack-merge");
const path = require("path");
const baseWebpackConfig = require("./webpack.base.conf");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
const portfinder = require("portfinder");

const HOST = process.env.HOST;
const PORT = process.env.PORT && Number(process.env.PORT);
const baseFileName = require(".. /package.json").name;
function resolve(dir) {
  return path.join(__dirname, "..", dir);
}
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.dev.cssSourceMap,
      extract: true.usePostCSS: true})},// cheap-module-eval-source-map is faster for development
  devtool: config.dev.devtool,
  output: {
    path: resolve(config.dev.assetsRoot),
    filename: "static/" + baseFileName + "/js/[name]-[hash:5].js".chunkFilename: "static/" + baseFileName + "/js/[name]-[id:5].js"
  },

  plugins: [
    new webpack.DefinePlugin({
      "process.env": require(".. /config/dev.env")}),// new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: resolve(`.. /view/blog/index_index.html`),
      template: "./view/index.html".title: "Blog Management System".favicon: resolve("favicon.ico"),
      inject: true.chunks: ["manifest"."vendor"."app"]}),new HtmlWebpackPlugin({
      filename: resolve(`.. /view/blog/blog_index.html`),
      template: "./view/blog.html".title: "Blog presentation".inject: true.chunks: ["manifest"."vendor"."blog"]}),// copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, ".. /static"),
        to: config.dev.assetsSubDirectory,
        ignore: [". *"[}])]});module.exports = devWebpackConfig;


Copy the code

Here I deleted the default devSever configuration and exported devWebpackConfig directly from Module. exports. In addition, because the package to generate the change of the directory, I changed inside the output path and filename, chunkFilename. Make them generated under the WWW /static root of self_blog, which is also the default directory for thinkJs static resources. Note that filename refers to your export file (app) and the filename of the js (vendor,manifest) generated by codespliting. ChunkFilename defines the JS file names of lazily loaded components.

The next thing you need to add is the HTml-webpack-plugin, which is used twice because it needs to be packaged into two HTML files. In addition to defining the generation filename, HTML template, etc., you also need to define the chunk you need for the HTML, which is a difference from the single-page configuration.

In addition, if you want the CSS to be isolated when your development environment is packaged, set extract to true when using utils.styleloaders. Because the method here determines that the development environment uses extract-text-plugin to extract CSS.

Webpack. Prod. Conf. Js configuration

The configuration of Prod is similar to dev, and the generated file directory is the same, but there are some more JS, CSS compression plug-ins.

"use strict";
const path = require("path");
const utils = require("./utils");
const webpack = require("webpack");
const config = require(".. /config");
const merge = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const baseFileName = require(".. /package.json").name;
const env = require(".. /config/prod.env");
function resolve(dir) {
  return path.join(__dirname, "..", dir);
}
const webpackConfig = merge(baseWebpackConfig, {
  entry: {
    app: "./entry/entry-client-index".blog: "./entry/entry-client-blog"
  },
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true.usePostCSS: true})},devtool: config.build.productionSourceMap ? config.build.devtool : false.output: {
    path: resolve(config.dev.assetsRoot),
    filename: "static/" + baseFileName + "/js/[name]-[chunkhash:5].js".chunkFilename: "static/" + baseFileName + "/js/[name]-[chunkhash:5].js"
  },
  plugins: [
    new webpack.DefinePlugin({
      "process.env": require(".. /config/prod.env"),
      "process.env.VUE_ENV": '"client"'
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false}},sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath(
        baseFileName + "/css/[name].[contenthash].css"
      ),
      allChunks: true
    }),
    new HtmlWebpackPlugin({
      minify: {},
      chunksSortMode: "dependency".environment: process.env.NODE_ENV,
      filename: resolve(`.. /view/blog/index_index.html`),
      template: "./view/index.html".title: "Blog Management System".favicon: resolve("favicon.ico"),
      inject: true.chunks: ["manifest"."vendor"."app"]}),new HtmlWebpackPlugin({
      filename: resolve(`.. /view/blog/blog_index.html`),
      template: "./view/blog.html".title: "Blog presentation".favicon: resolve("favicon.ico"),
      inject: true.chunks: ["manifest"."vendor"."blog"]}),// Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true.map: { inline: false}}, {safe: true}})]});if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require("compression-webpack-plugin");

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: "[path].gz[query]".algorithm: "gzip".test: new RegExp(
        "\ \. (" + config.build.productionGzipExtensions.join("|") + "$"
      ),
      threshold: 10240.minRatio: 0.8})); }if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
    .BundleAnalyzerPlugin;
  webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}

module.exports = webpackConfig;

Copy the code

conclusion

See the github code for details. The main purpose of this project is to practice thinkJs. Here, we will briefly talk about the early WebPack configuration, and in the next chapter, we will talk about the usage methods and features of thinkJs in detail. The project will continue to be updated, many things are not finished.