资讯专栏INFORMATION COLUMN

从零开始做Vue前端架构(5)

feng409 / 495人阅读

摘要:前言弄完了前后端分离,我们自然想打包发布项目了。下篇我们会来完成测试的流程从零开始做前端架构项目完整代码前端架构子咻

前言

弄完了前后端分离,我们自然想打包发布项目了。
不多说,就让我们来看看吧。

开发

直接上代码:

const webpack = require("webpack")
const path = require("path")
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const webpackConfigBase = require("./webpack.config.js")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin
const exec = require("child_process").execSync
const pkg = require("./package.json")
// 为了抽离出两份CSS,创建两份ExtractTextPlugin
// base作为基础的css,基本不变,所以,可以抽离出来充分利用浏览器缓存
// app作为迭代的css,会经常改变
const extractBaseCSS = new ExtractTextPlugin({filename:"static/css/base.[chunkhash:8].css", allChunks: true})
const extractAppCSS = new ExtractTextPlugin({filename:"static/css/app.[chunkhash:8].css", allChunks: true})

// 减少路径书写
function resolve(dir) {
  return path.join(__dirname, dir)
}
// 网站图标配置
const favicon = resolve("favicon.ico")
// 网站版本号设置
let appVersion = ""
try {
  appVersion = exec("git rev-parse --short HEAD").toString().replace(/
/, "")
} catch (e) {
  console.warn("Getting revision FAILED. Maybe this is not a git project.")
}

const config = Object.assign(webpackConfigBase, {
  // You should configure your server to disallow access to the Source Map file for normal users!
  devtool: "source-map",
  entry: {
    app: resolve("app/index.js"),
    // 将第三方依赖(node_modules)的库打包,从而充分利用浏览器缓存
    vendor: Object.keys(pkg.dependencies)
  },
  output: {
    path: resolve("dist"),
    // publicPath: "https://cdn.self.com"
    publicPath: resolve("dist/"),
    filename: "static/js/[name].[chunkhash:8].js"
  },
  module: {
    rules: [
      {
        test: /.js$/,
        include: [resolve("app")],
        loader: "babel-loader"
      },
      {
        test: /.vue$/,
        exclude: /node_modules/,
        loader: "vue-loader",
        options: {
          extractCSS: true,
          loaders: {
            scss: extractAppCSS.extract({
              fallback: "vue-style-loader",
              use: [
                {
                  loader: "css-loader",
                  options: {
                    sourceMap: true
                  }
                },
                {
                  loader: "postcss-loader",
                  options: {
                    sourceMap: true
                  }
                },
                {
                  loader: "sass-loader",
                  options: {
                    sourceMap: true
                  }
                }
              ]
            })
          }
        }
      },
      {
        test: /.(css|scss)$/,
        use: extractBaseCSS.extract({
          fallback: "style-loader",
          use: [
            {
              loader: "css-loader",
              options: {
                sourceMap: true
              }
            },
            {
              loader: "postcss-loader",
              options: {
                sourceMap: true
              }
            },
            {
              loader: "sass-loader",
              options: {
                sourceMap: true
              }
            }
          ]
        })
      },
      {
        test: /.(png|jpe?g|gif|svg|ico)(?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: "static/img/[name].[hash:8].[ext]"
        }
      },
      {
        test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: "static/font/[name].[hash:8].[ext]"
        }
      }
    ]
  },
  plugins: [
    // Scope hosting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 删除build文件夹
    new CleanWebpackPlugin(
      resolve("dist")
    ),
    // 抽离出css
    extractBaseCSS,
    extractAppCSS,
    // 提供公共代码vendor
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      filename: "static/js/[name].[chunkhash:8].js"
    }),
    // html 模板插件
    new HtmlWebpackPlugin({
      appVersion,
      favicon,
      filename: "index.html",
      template: resolve("app/index.html"),
      minify: {
        removeComments: true,
        collapseWhitespace: false
      }
    }),
    // 定义全局常量
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: ""production""
      }
    }),
    // 可视化分析
    new BundleAnalyzerPlugin(),
    // 加署名
    new webpack.BannerPlugin("Copyright by 子咻 https://github.com/CodeLittlePrince/blog"),
  ]
})

module.exports = config

代码几乎全都有注释,有不懂的可以在评论去留言。

思考

虽然代码写好了,但是我们不禁发出一声“卧槽”,好多和webpack.config.js一样的代码啊,要是改了一样的代码部分,我还得同时改两份,而且,这么多的冗余代码对于一个优秀的程序员来讲,是不可容忍的。
那我们改怎么呢?

重构webpack的配置代码 一、创建一个基础的webpack配置文件

我们就叫webapck.config.base.js吧:

const path = require("path")

// 为了抽离出两份CSS,创建两份ExtractTextPlugin
// base作为基础的css,基本不变,所以,可以抽离出来充分利用浏览器缓存
// app作为迭代的css,会经常改变
const isProduction = process.env.NODE_ENV === "production"
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const extractBaseCSS =
  new ExtractTextPlugin(
    {
      filename:"static/css/base.[chunkhash:8].css",
      allChunks: true,
      disable: !isProduction // 开发环境下不抽离css
    }
  )
const extractAppCSS
  = new ExtractTextPlugin(
    {
      filename:"static/css/app.[chunkhash:8].css",
      allChunks: true,
      disable: !isProduction // 开发环境下不抽离css
    }
  )

// 减少路径书写
function resolve(dir) {
  return path.join(__dirname, dir)
}

// 网站图标配置
const favicon = resolve("favicon.ico")

// __dirname: 总是返回被执行的 js 所在文件夹的绝对路径
// __filename: 总是返回被执行的 js 的绝对路径
// process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径
const config = {
  resolve: {
    // 扩展名,比如import "app.vue",扩展后只需要写成import "app"就可以了
    extensions: [".js", ".vue", ".scss", ".css"],
    // 取路径别名,方便在业务代码中import
    alias: {
      api: resolve("app/api/"),
      common: resolve("app/common/"),
      views: resolve("app/views/"),
      components: resolve("app/components/"),
      componentsBase: resolve("app/componentsBase/"),
      directives: resolve("app/directives/"),
      filters: resolve("app/filters/"),
      mixins: resolve("app/mixins/")
    }
  },
  // loaders处理
  module: {
    rules: [
      {
        test: /.js$/,
        include: [resolve("app")],
        loader: [
          "babel-loader",
          "eslint-loader"
        ]
      },
      {
        test: /.vue$/,
        exclude: /node_modules/,
        loader: "vue-loader",
        options: {
          extractCSS: true,
          loaders: {
            scss: extractAppCSS.extract({
              fallback: "vue-style-loader",
              use: [
                {
                  loader: "css-loader",
                  options: {
                    sourceMap: true
                  }
                },
                {
                  loader: "postcss-loader",
                  options: {
                    sourceMap: true
                  }
                },
                {
                  loader: "sass-loader",
                  options: {
                    sourceMap: true
                  }
                }
              ]
            })
          }
        }
      },
      {
        test: /.(css|scss)$/,
        use: extractBaseCSS.extract({
          fallback: "style-loader",
          use: [
            {
              loader: "css-loader",
              options: {
                sourceMap: true
              }
            },
            {
              loader: "postcss-loader",
              options: {
                sourceMap: true
              }
            },
            {
              loader: "sass-loader",
              options: {
                sourceMap: true
              }
            }
          ]
        })
      },
      {
        test: /.(png|jpe?g|gif|svg|ico)(?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: isProduction
            ? "static/img/[name].[hash:8].[ext]"
            : "static/img/[name].[ext]"
        }
      },
      {
        test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: isProduction
            ? "static/font/[name].[hash:8].[ext]"
            : "static/font/[name].[ext]"
        }
      }
    ]
  }
}

module.exports = {
  config,
  favicon,
  resolve,
  extractBaseCSS,
  extractAppCSS
}
二、重构webpack开发环境配置
const webpack = require("webpack")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin")
const webpackConfigBase = require("./webpack.config.base.js")

const config = Object.assign(webpackConfigBase.config, {
  // sourcemap 模式
  devtool: "cheap-module-eval-source-map",
  // 入口
  entry: {
    app: [
      "babel-polyfill", // 这里是配合babel-present-env导入的动态babel-polyfill,因此npm需dev依赖
      webpackConfigBase.resolve("app/index.js")
    ]
  },
  // 输出
  output: {
    path: webpackConfigBase.resolve("dev"),
    filename: "index.bundle.js"
  },
  plugins: [
    // html 模板插件
    new HtmlWebpackPlugin({
      favicon: webpackConfigBase.favicon,
      filename: "index.html",
      template: webpackConfigBase.resolve("app/index.html")
    }),
    // 抽离出css,开发环境其实不抽离,但是为了配合extract-text-webpack-plugin插件,需要做个样子
    webpackConfigBase.extractAppCSS,
    webpackConfigBase.extractBaseCSS,
    // 热替换插件
    new webpack.HotModuleReplacementPlugin(),
    // 更友好地输出错误信息
    new FriendlyErrorsPlugin()
  ],
  devServer: {
    proxy: {
      // 凡是 `/api` 开头的 http 请求,都会被代理到 localhost:7777 上,由 koa 提供 mock 数据。
      // koa 代码在 ./mock 目录中,启动命令为 npm run mock。
      "/api": {
        target: "http://localhost:7777", // 如果说联调了,将地址换成后端环境的地址就哦了
        secure: false
      }
    },
    host: "0.0.0.0",
    port: "9999",
    disableHostCheck: true, // 为了手机可以访问
    contentBase: webpackConfigBase.resolve("dev"), // 本地服务器所加载的页面所在的目录
    // historyApiFallback: true, // 为了SPA应用服务
    inline: true, //实时刷新
    hot: true  // 使用热加载插件 HotModuleReplacementPlugin
  }
})

module.exports = config
三、重构webpack开发环境配置
const webpack = require("webpack")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin
const exec = require("child_process").execSync
const webpackConfigBase = require("./webpack.config.base.js")
const pkg = require("./package.json")

// 网站版本号设置
let appVersion = ""
try {
  appVersion = exec("git rev-parse --short HEAD").toString().replace(/
/, "")
} catch (e) {
  console.warn("Getting revision FAILED. Maybe this is not a git project.")
}

const config = Object.assign(webpackConfigBase.config, {
  // You should configure your server to disallow access to the Source Map file for normal users!
  devtool: "source-map",
  entry: {
    app: webpackConfigBase.resolve("app/index.js"),
    // 将第三方依赖(node_modules)的库打包,从而充分利用浏览器缓存
    vendor: Object.keys(pkg.dependencies)
  },
  output: {
    path: webpackConfigBase.resolve("dist"),
    // publicPath: "https://cdn.self.com"
    publicPath: webpackConfigBase.resolve("dist/"),
    filename: "static/js/[name].[chunkhash:8].js"
  },
  plugins: [
    // Scope hosting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 删除build文件夹
    new CleanWebpackPlugin(
      webpackConfigBase.resolve("dist")
    ),
    // 抽离出css
    webpackConfigBase.extractAppCSS,
    webpackConfigBase.extractBaseCSS,
    // 提取公共代码vendor
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      filename: "static/js/[name].[chunkhash:8].js"
    }),
    // html 模板插件
    new HtmlWebpackPlugin({
      appVersion,
      favicon: webpackConfigBase.favicon,
      filename: "index.html",
      template: webpackConfigBase.resolve("app/index.html"),
      minify: {
        removeComments: true,
        collapseWhitespace: false
      }
    }),
    // 定义全局常量
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: ""production""
      }
    }),
    // 可视化分析
    new BundleAnalyzerPlugin(),
    // 加署名
    new webpack.BannerPlugin("Copyright by 子咻 https://github.com/CodeLittlePrince/blog"),
  ]
})

module.exports = config

代码瞬间变得清晰、精简、高大上有没有?!(^-^)V

看一下打包处理后代码情况(兼容IE10及以上):

总结

这一篇我们编写了开发环境用的webpack配置文件,然后发现代码的冗余从而重构了开发和发布环境的webpack配置。

之后,我们还需要能够自动测试我们写的业务代码,避免人工手动各种戳页面(虽然大部分公司都是这么干的,即使是大公司会腾出时间和人手写测试用例的部门也不多),不过架构还是要做的。

下篇我们会来完成测试的流程 - 从零开始做Vue前端架构(6)

项目完整代码

Vue前端架构-by 子咻

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/11836.html

相关文章

  • 从零开始Vue前端架构(4)

    摘要:前言上一篇我们遇到少年,是不是忘了的警告,这一篇我们就来解决这个问题。总结通过实现前后端分离,并且使用来更方便的模拟数据。下一篇,我们创建发布环境下的配置文件,并且看看怎么优化产出的代码的从零开始做前端架构项目完整代码前端架构子咻 前言 上一篇我们遇到少年,是不是忘了npm run mock?的警告,这一篇我们就来解决这个问题。 开发 一、安装包 安装koa和一系列的包(我们用的是ko...

    xuweijian 评论0 收藏0
  • 从零开始Vue前端架构(3)

    摘要:前言这一篇,我们将接着上篇来完成创建项目文件目录架构。总结这篇主要是文件和目录架构的东西,读者务必运行一下完整的项目。因此,下一篇,我们将通过实现本地数据接口模拟从零开始做前端架构项目完整代码前端架构子咻 前言 这一篇,我们将接着上篇来完成创建项目文件、目录架构。 回顾 先回顾一下现在项目有哪些东西了: . ├── app │   ├── app.vue │   ├── common ...

    dinfer 评论0 收藏0
  • 从零开始Vue前端架构(1)

    摘要:前言想想也已经做过不少架构的项目了,有基于,基于,基于,基于的。好了,介绍完毕,接下来,我就从零开始,一步一步建起前后端完全分离的前端架构了。 前言 想想也已经做过不少架构的项目了,有基于vue,基于react,基于thinkPHP,基于laravel的。 做多了,也就对现有的架构有各种想法,有好的,有坏的,总之,用起来还是不爽。vue-cli虽然可以很快地构建并使用,尤其是vue-c...

    frolc 评论0 收藏0
  • 从零开始Vue前端架构(2)

    摘要:前言这一篇,我们将接着上篇来完成配置。开发一配置我们采用的方式来创建。对了,前提我们需要全局安装。三配置创建文件,上配置配置总结这篇不多,就做了三件事,。下一篇我们将创建项目文件目录架构从零开始做前端架构项目完整代码前端架构子咻 前言 这一篇,我们将接着上篇来完成配置eslint、babel、postcss。 开发 一、配置eslint 我们采用eslint --init的方式来创建e...

    Brenner 评论0 收藏0
  • 从零开始Vue前端架构(2)

    摘要:前言这一篇,我们将接着上篇来完成配置。开发一配置我们采用的方式来创建。对了,前提我们需要全局安装。三配置创建文件,上配置配置总结这篇不多,就做了三件事,。下一篇我们将创建项目文件目录架构从零开始做前端架构项目完整代码前端架构子咻 前言 这一篇,我们将接着上篇来完成配置eslint、babel、postcss。 开发 一、配置eslint 我们采用eslint --init的方式来创建e...

    YanceyOfficial 评论0 收藏0
  • 从零开始Vue前端架构(9)

    摘要:那该怎么管理这两个不同的项目呢解决子模块用的的同学肯定一下子就想到子模块的知识了。最后,也希望有想法的同学还有大佬多多留言,给点建议原文地址从零开始做前端架构脚手架参考资料官方文档使用定制前端脚手架别人写的脚手架文件操作相关文档子模块 前言 相信很多人都用过vue-cli或create-react-app或者类似的脚手架。脚手架方便我们复制,粘贴,或者clone代码库,而且还可以更具用...

    Vicky 评论0 收藏0

发表评论

0条评论

feng409

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<