从vue-cli中看node.js和webpack的运作
来源:互联网 发布:淘宝客分佣app制作 编辑:程序博客网 时间:2024/05/18 19:19
前言
vue-cli和webpack结合的脚手架挺好用的,但是初次使用对于其中的配置和npm包的引用总是会一脸懵逼,这篇文章是对其中一些相关模块的简单分析。
主要目的是加深我自己对webpack和node.js的认知。
正文
1.项目结构
vue-cli的配置文件主要在build和config文件夹中,其中config文件夹主要是放一些环境变量,webpack的路径等等一些参数。
| -- build | -- build.js | -- check-version.js | -- dev-client.js | -- dev-server.js | -- utils.js | -- vue-loader.conf.js | -- webpack.base.conf.js | -- webpack.dev.conf.js | -- webpack.prod.conf.js | -- webpack.test.conf.js| -- config | -- dev.env.js | -- index.js | -- prod.env.js | -- test.env.js......
2.从npm run dev开始
本地调试一般需要热重载,创建本地服务器,所以我们常使用的命令是npm run dev
,根据package.json
得知实际上执行的是node build/dev-server.js
命令。
dev-server.js
好吧,让我们看看dev-server.js到底做了什么。
说实话做的就是,利用express创建一个服务,监听特定的端口,利用express().use()使用wepack的中间件。可以改一改代理,是从本地localhost获取数据改为从其他代理地址获取数据。
// 这是一个版本检测脚本,主要是测试本地的npm和node版本是否要求require('./check-versions')()// ./check-versions.js// 引入三个包,chalk(给字符串在命令行添加颜色)、semver(处理版本号字符串),child_process(添加个子进程)var chalk = require('chalk')var semver = require('semver')// 引入package.json中的node和npm要求版本var packageConfig = require('../package.json')// child_process.execSync(cmd) 相同于同步在命令行执行cmd命令function exec (cmd) { return require('child_process').execSync(cmd).toString().trim()}// 把信息都放入对象var versionRequirements = [ { name: 'node', currentVersion: semver.clean(process.version), versionRequirement: packageConfig.engines.node }, { name: 'npm', currentVersion: exec('npm --version'), versionRequirement: packageConfig.engines.npm }]// 输出一个函数module.exports = function () { var warnings = [] for (var i = 0; i < versionRequirements.length; i++) { var mod = versionRequirements[i] // semver工具比较版本是否符合 if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { // 不符合,则添加到warning数组 warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement) ) } } // warning不为空,命令界面输出warning if (warnings.length) { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log() for (var i = 0; i < warnings.length; i++) { var warning = warnings[i] console.log(' ' + warning) } console.log() // 强制终结所有相关进程(也就是说如果版本号不符合,直接跳出进程) process.exit(1) }}
接下来继续看干了什么:
// 引入opn包(利用默认浏览器打开uri路径),express(web框架)var opn = require('opn')var express = require('express')// 搭建本地的服务器var app = express()// 引入config配置对象var config = require('../config')// 进程没有NODE_ENV的话,设置("production";"develpoment"或"testing")if (!process.env.NODE_ENV) { process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)}// 端口号,没有默认值则使用配置值(8080)var port = process.env.PORT || config.dev.port// config配置对象里默认为truevar autoOpenBrowser = !!config.dev.autoOpenBrowser// app监听端口,成功后由回调(是否打开网页)module.exports = app.listen(port, function (err) { if (err) { console.log(err) return } // when env is testing, don't need open it if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { opn(uri) }})
好了,现在express()监听了端口,也可以自动打开了,但是里面没内容啊,而且热重载之类的是怎么做到的呢?
// 本地热重载的url路径var uri = 'http://localhost:' + port// 根据process.env.NODE_ENV选择加载不同的webpack配置var webpackConfig = process.env.NODE_ENV === 'testing' ? require('./webpack.prod.conf') : require('./webpack.dev.conf')var webpack = require('webpack') var compiler = webpack(webpackConfig)// webpack-dev-middler是为webpack准备的中间件,服务webpack在连接服务器上导出的文件,开发专用// 只写在内存中,而不会占用硬盘空间// 如果文件发生改动,该中间件不再服务旧的打包文件,反而延迟请求直到编译结束。所以你没必要在因文件改动而刷新页面前等待。// Usage: app.use(webpackMiddleWare(webpack({}))var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, quiet: true})app.use(devMiddleware)// 编译结束后回调devMiddleware.waitUntilValid(function () { console.log('> Listening at ' + uri + '\n')})// 这个模块只和连接客户端与webpack服务器以及接受更新相关。它会接受服务器的更新然后执行这些更新。var hotMiddleware = require('webpack-hot-middleware')(compiler, { log: () => {}})app.use(hotMiddleware)// 当html-webpack-plugin模板发生改变的时候,强制热加载compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() })})// 静态文件的输出路径,这里是"/static"var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)// 如果希望所有通过express.static访问的文件都存放在一个虚拟目录下面,可以通过为静态资源目录制定一个挂载路径的方式来实现,如下:app.use(staticPath, express.static('./static'))
还有一些。
// 单线程的代理中间件var proxyMiddleware = require('http-proxy-middleware')// 引入代理表,这里为空对象var proxyTable = config.dev.proxyTable/*** 关于这个模块的用法* var express = require('express');* var proxy = require('http-proxy-middleware');* * var app = express()** app.user('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));* app.listen(3000);* http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar* 上面这个意思是,/api这个请求会被中间件导向目标host,支持正则表达*/Object.keys(proxyTable).forEach(function (context) { var options = proxyTable[context] if (typeof options === 'string') { options = { target: options } } // 所有请求都将被导向新的host app.use(proxyMiddleware(options.filter || context, options))})// 针对单页面应用的H5历史API回调// 单页面应用通常只使用一个index文件作为html文件,所以正常情况下后退浏览器会发生404。app.use(require('connect-history-api-fallback')())
3.看一看webpack.conf配置
先了解一下merge模块。
var merge = require('webpack-merge')module.exports = merge(baseWebpackConfig, {})
utils.js
再看一下工具类里面的函数。
// 这个模块的作用是把文本从bundle中提取出来放入一个单独的文件中// 用法// const ExtractTextPlugin = require("extract-text-webpack-plugin");// module.exports = {// module: {// rules: [// {// test: /\.css$/,// use: ExtractTextPlugin.extract({// fallback: "style-loader",// use: "css-loader"// })// }// ]// },// plugins: [// new ExtractTextPlugin("styles.css"),// ]// }// 上面这个的意思是把所有的*.css模块提取出来放到一个单独的文件中(styles.css),css打包和js打包是相互独立的var ExtractTextPlugin = require('extract-text-webpack-plugin')// 返回一个资源路径字符串,子目录下的路径exports.assetsPath = function (_path) { var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path)}/***** @************************ @return: {*** css: [ 'vue-style-loader', [ { loader: 'css-loader', options: { minimize: process.env.NODE_ENV === 'production', sourceMap: options.sourceMap } } ] ]*** }*** */exports.cssLoaders = function (options) { options = options || {} var cssLoader = { loader: 'css-loader', options: { minimize: process.env.NODE_ENV === 'production', sourceMap: options.sourceMap } } // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { var loaders = [cssLoader] if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } // Extract CSS when that option is specified // (which is the case during production build) if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader' }) } else { return ['vue-style-loader'].concat(loaders) } } // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') }}// Generate loaders for standalone style files (outside of .vue)exports.styleLoaders = function (options) { var output = [] var loaders = exports.cssLoaders(options) for (var extension in loaders) { var loader = loaders[extension] output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output}
webpack.base.conf
var path = require('path')// 工具类var utils = require('./utils')var config = require('../config')// vue-loader的options配置var vueLoaderConfig = require('./vue-loader.conf')// 返回dir的绝对路径位置function resolve (dir) { return path.join(__dirname, '..', dir)}
上面定义了一些工具,具体配置如下:
const webpackConfig = { // 入口文件,webpack从这个文件开始打包所有相关文件 entry: { app: './src/apps/index.js' }, // 输出路径 // 这里path: '../dist' // publicPath: '/' // '../dist/' output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, // extensions,扩展名是这些的文件,引用的时候可以省略 // alias,别名 resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), 'apps': path.resolve(__dirname, '../src/apps'), 'common': path.resolve(__dirname, '../src/common'), } }, // 模块规则,webpack可把其他资源(其他格式的文件)转换为js,利用的就是loader module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', query: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', query: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }}
webpack.dev.conf
开发的配置主要是基础配置再合并了特殊的开发配置对象(主要是插件)。
// 这个webpack插件简单得创建了一些HTML文件来服务bundlesvar HtmlWebpackPlugin = require('html-webpack-plugin')var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')module.exports = merge(baseWebpackConfig, { //exports.styleLoaders = function (options) { // var output = [] // var loaders = exports.cssLoaders(options) // for (var extension in loaders) { // var loader = loaders[extension] // output.push({ // test: new RegExp('\\.' + extension + '$'), // use: loader // }) // } // return output //} module: { rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) }, devtool: '#cheap-module-eval-source-map', plugins: [ new webpack.DefinePlugin({ 'process.env': config.dev.env }), new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin(), new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true, chunks: ['app'] }), new FriendlyErrorsPlugin() ]})
webpack.prod.conf
var CopyWebpackPlugin = require('copy-webpack-plugin')var HtmlWebpackPlugin = require('html-webpack-plugin')var ExtractTextPlugin = require('extract-text-webpack-plugin')// 自动在webpack构建的过程中搜索css资源并压缩优化它们var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
webpack.test.conf
3.npm run build
运行这行命令其实就是 node build/build.js
。
build.js
require('./check-versions')()process.env.NODE_ENV = 'production'// ora模块就是在终端显示优雅的旋转等待符号var ora = require('ora')var spinner = ora('building for production...')spinner.start()spinner.stop()
var rm = require('rimraf')rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err webpack(webpackConfig, function (err, stats) { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + '\n\n') console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) })})
阅读全文
0 0
- 从vue-cli中看node.js和webpack的运作
- vue-cli的webpack模版,相关配置文件dev-server.js与webpack.config.js配置解析
- vue-cli和webpack项目搭建
- vue-cli webpack在node环境下安装使用详解
- 基于vue-cli的Webpack构建
- 安装node和vue-cli 并从GitHub上下载vue开源项目 并运行
- Vue-cli+router+webpack
- webpack与vue-cli
- vue环境搭建(一)webpack和vue-cli安装
- vue-cli + webpack + vue-router
- 史上最详细的 webpack 讲解 1 (vue-cli 中 build.js)
- vue-cli&webpack&arcgis API For JS的天坑之路(一)
- vue-cli&webpack&arcgis API For JS的天坑之路(二)
- 史上最详细的 webpack 讲解 1 (vue-cli 中 build.js)
- 用Vue-cli生成vue+webpack的项目模板
- webpack + vue-cli 搭建 vue 项目的流程
- node.js中看javascript的prototype
- vue的官方脚手架vue-cli到底做了什么?(vue-cli webpack配置详解)
- 欢迎使用CSDN-markdown编辑器
- 图像旋转c++实现
- 用Python开始机器学习(4:KNN分类算法)
- LeetCode 502. IPO
- 获取图片大小
- 从vue-cli中看node.js和webpack的运作
- 如何在登陆后返回之前浏览的页面
- 动态web技术(二) --- CGI
- linux内核树的构建
- MVC、MVP、MVVM
- TabError的解决方法
- PHP数组排序函数
- 【Java web】利用eclipse打开并调试Java web项目,包括部署tomcat,连接数据库
- Digital Roots