如何打造一个令人愉悦的前端开发环境(二)

开发 开发工具
这篇文章主要讲目前火热的打包构建方式--Webpack的使用方式。其实Webpack的入门指导文章非常多,配置方式也各有各样,如果不知道Webpack是什么或者不是很清楚各项配置含义的开发者,可以先看叶大神的入门级指南--Webpack 入门指迷。

[[169799]]

前情提要

上一篇文章介绍了目前前端比较流行的各种编辑器,以及各种流行的打包方式,***给了一个Gulp的例子,这个例子还是14年的时候写的,还有一些可以优化的空间,就不讨论了,这篇文章主要讲目前火热的打包构建方式--Webpack的使用方式。

主菜--没有开胃汤

其实Webpack的入门指导文章非常多,配置方式也各有各样,这里我推荐题叶大神的入门级指南--Webpack 入门指迷,如果不知道Webpack是什么或者不是很清楚各项配置含义的开发者,可以看此文章扫扫盲。毕竟我这篇文章并不是特别基础。

一、base.js

  1. var path = require('path'
  2. var baseConfig = { 
  3.     resolve: { 
  4.         extensions: ['''.js'], 
  5.         fallback: [path.join(__dirname, '../node_modules')], 
  6.         alias: { 
  7.             'src': path.resolve(__dirname, '../src'), 
  8.             'assets': path.resolve(__dirname, '../src/assets'), 
  9.             'components': path.resolve(__dirname, '../src/components'
  10.         } 
  11.     }, 
  12.     module: { 
  13.         loaders: [{ 
  14.             test: /\.js$/, 
  15.             loader: 'babel'
  16.             exclude: /node_modules/ 
  17.         }, { 
  18.             test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/, 
  19.             loader: 'url?limit=8192&context=client&name=[path][name].[hash:7].[ext]' 
  20.         }, 
  21.         { 
  22.             test: /\.css$/, 
  23.             loader: 'style!css!autoprefixer'
  24.         }, 
  25.         { 
  26.             test: /\.scss$/, 
  27.             loader: 'style!css!autoprefixer!sass' 
  28.         }] 
  29.     } 
  30. }; 
  31.  
  32. module.exports = baseConfig; 

解读下这个基本配置:

1、resolve 解析模块依赖的时候,受影响的配置项。

extensions 决定了哪些文件后缀在引用的时候可以省略点,Webpack帮助你补全名称。

fallback 当webpack在 root(默认当前文件夹,配置时要绝对路径) 和 modulesDirectories(默认当前文件夹,相对路径)配置下面找不到相关modules,去哪个文件夹下找modules

alias 这个大家应该比较熟悉,requirejs之类的都有,就是别名,帮助你快速指向文件路径,少写不少代码,而且不用关心层级关系,需要注意的是:在scss之类的css预编译中引用要加上~,以便于让loader识别是别名引用路径。

2、module 解析不同文件使用哪些loader,这个比较简单,很多文章都有,就不多说了,注意的是,这里的scss可以换成你自己的预编译器,例如:sass、less、stylus等,或者直接用postcss都行,当然还可以用一种通用方法,后面补上。

二、开发环境配置--config

  1. var webpack = require('webpack'); 
  2. var path = require('path'
  3. var merge = require('webpack-merge'
  4. var baseConfig = require('./webpack.base'
  5. var getEntries = require('./getEntries'
  6.  
  7. var hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'
  8.  
  9. var assetsInsert = require('./assetsInsert'
  10.  
  11. module.exports = merge(baseConfig, { 
  12.   entry: getEntries(hotMiddlewareScript), 
  13.   devtool: '#eval-source-map'
  14.   output: { 
  15.     filename: './[name].[hash].js'
  16.     path: path.resolve('./dist'), 
  17.     publicPath:'./dist' 
  18.   }, 
  19.   plugins: [ 
  20.     new webpack.DefinePlugin({ 
  21.       'process.env': { 
  22.         NODE_ENV: '"development"' 
  23.       } 
  24.     }), 
  25.     new webpack.optimize.OccurenceOrderPlugin(), 
  26.     new webpack.HotModuleReplacementPlugin(), 
  27.     new webpack.NoErrorsPlugin(), 
  28.     new assetsInsert() 
  29.   ] 
  30. }) 

说说这个配置中的一些难点:

1、getEntries 是用来配置入口文件,一般很多人是自己手写,或者SPA页面,只有一个入口, 很容易就写出来,但是公司中,很多情况,是需要多入口,也就是多路由的Url,这个时候入口的配置就比较麻烦,我这里是放单独一个文件里面配置,我们公司是靠规定来执行,也就是一个文件夹所有的main.js都认为是入口文件,其他都忽略。

  1. function getEntry(hotMiddlewareScript) { 
  2.     var pattern = paths.dev.js + 'project/**/main.js'
  3.     var array = glob.sync(pattern); 
  4.     var newObj = {}; 
  5.  
  6.     array.map(function(el){ 
  7.         var reg = new RegExp('project/(.*)/main.js','g'); 
  8.         reg.test(el); 
  9.         if (hotMiddlewareScript) { 
  10.             newObj[RegExp.$1] = [el, hotMiddlewareScript]; 
  11.         } else { 
  12.             newObj[RegExp.$1] = el; 
  13.         } 
  14.     }); 
  15.     return newObj; 

2、assetsInsert 是用来做模板替换的,一个小插件把template里面的值替换成打包后的css或者js。

三、打包环境配置--production

  1. var webpack = require('webpack'); 
  2. var path = require('path'
  3. var merge = require('webpack-merge'
  4. var baseConfig = require('./webpack.base'
  5. var getEntries = require('./getEntries'
  6. var ExtractTextPlugin = require('extract-text-webpack-plugin'); 
  7. var assetsInsert = require('./assetsInsert'
  8.  
  9. var productionConf = merge(baseConfig, { 
  10.     entry: getEntries(), 
  11.     output: { 
  12.         filename: './[name].[hash].js'
  13.         path: path.resolve('./public/dist'), 
  14.         publicPath: './' 
  15.     }, 
  16.     plugins: [ 
  17.         new webpack.DefinePlugin({ 
  18.             'process.env': { 
  19.                 NODE_ENV: '"production"' 
  20.             } 
  21.         }), 
  22.         new ExtractTextPlugin('./[name].[hash].css', { 
  23.             allChunks: true 
  24.         }), 
  25.         new webpack.optimize.UglifyJsPlugin({ 
  26.             compress: { 
  27.                 warnings: false 
  28.             } 
  29.         }), 
  30.         new webpack.optimize.OccurenceOrderPlugin(), 
  31.         new assetsInsert() 
  32.     ] 
  33. }) 
  34.  
  35. productionConf.module.loaders = [ 
  36.              { 
  37.                 test: /\.js$/, 
  38.                 loader: 'babel'
  39.                 exclude: /node_modules/ 
  40.             }, { 
  41.                 test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/, 
  42.                 loader: 'url?limit=8192&context=client&name=[path][name].[hash:7].[ext]' 
  43.             }, 
  44.             { 
  45.                 test: /\.css$/, 
  46.                 loader: ExtractTextPlugin.extract('style''css'), 
  47.             }, 
  48.             { 
  49.                 test: /\.scss$/, 
  50.                 loader: ExtractTextPlugin.extract('style''css!sass'
  51.             }] 
  52.  
  53. module.exports = productionConf 

基本跟开发的差不多,差别在于:

1、使用ExtractTextPlugin 来打包css,所以要干掉原来base的loaders,重新写了一个,在最下面。

2、UglifyJsPlugin 给js压缩代码。其他没有什么好解释的了,一样的。

四、构建命令

  1. require('shelljs/global'
  2. env.NODE_ENV = 'production' 
  3. var ora = require('ora'
  4. var webpack = require('webpack'
  5. var webpackConfig = require('./webpack.production.config'
  6.  
  7. var spinner = ora('building for production...'
  8. spinner.start() 
  9.  
  10. var staticPath = __dirname + '/../public/dist/' 
  11. rm('-rf', staticPath) 
  12. mkdir('-p', staticPath) 
  13.  
  14. webpack(webpackConfig, function (err, stats) { 
  15.   spinner.stop() 
  16.   if (err) throw err 
  17.   process.stdout.write(stats.toString({ 
  18.     colors: true
  19.     modules: false
  20.     children: false
  21.     chunks: false
  22.     chunkModules: false 
  23.   }) + '\n'
  24. }) 

写一个build.js,然后在package.json里面添加 script 参数 

  1. "build""node build.js"//这里记得写自己build.js路径 

甜点(马卡龙)--有点腻

上面的配置是可以更改的,例如你在loaders 里面加上

  1.   test: /\.vue$/, 
  2.   loader: 'vue' 

就可以变成支持.vue文件的vuejs打包构建,同理,修改下支持jsx,和添加一些reactjs的module,就可以用来跑Reactjs的东西。

还有可以随意更改Css预编译器的类型,用你自己喜欢就行,或者跟我们前面提到的方法,把所有类型都配置上,

  1. var path = require('path'
  2. var config = require('../config'
  3. var ExtractTextPlugin = require('extract-text-webpack-plugin'
  4.  
  5. exports.assetsPath = function (_path) { 
  6.   return path.posix.join(config.build.assetsSubDirectory, _path) 
  7.  
  8. exports.cssLoaders = function (options) { 
  9.   options = options || {} 
  10.   // generate loader string to be used with extract text plugin 
  11.   function generateLoaders (loaders) { 
  12.     var sourceLoader = loaders.map(function (loader) { 
  13.       var extraParamChar 
  14.       if (/\?/.test(loader)) { 
  15.         loader = loader.replace(/\?/, '-loader?'
  16.         extraParamChar = '&' 
  17.       } else { 
  18.         loader = loader + '-loader' 
  19.         extraParamChar = '?' 
  20.       } 
  21.       return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : ''
  22.     }).join('!'
  23.  
  24.     if (options.extract) { 
  25.       return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 
  26.     } else { 
  27.       return ['vue-style-loader', sourceLoader].join('!'
  28.     } 
  29.   } 
  30.  
  31.   // http://vuejs.github.io/vue-loader/configurations/extract-css.html 
  32.   return { 
  33.     css: generateLoaders(['css']), 
  34.     postcss: generateLoaders(['css']), 
  35.     less: generateLoaders(['css''less']), 
  36.     sass: generateLoaders(['css''sass?indentedSyntax']), 
  37.     scss: generateLoaders(['css''sass']), 
  38.     stylus: generateLoaders(['css''stylus']), 
  39.     styl: generateLoaders(['css''stylus']) 
  40.   } 
  41.  
  42. // Generate loaders for standalone style files (outside of .vue) 
  43. exports.styleLoaders = function (options) { 
  44.   var output = [] 
  45.   var loaders = exports.cssLoaders(options) 
  46.   for (var extension in loaders) { 
  47.     var loader = loaders[extension] 
  48.     output.push({ 
  49.       test: new RegExp('\\.' + extension + '$'), 
  50.       loader: loader 
  51.     }) 
  52.   } 
  53.   return output 

这就是把所有的css预编译的都加到配置里面了。

总结下--买单啦

Webpack多种多样,就算一个loaders都有好几种不同的配置,让人很是头疼,最关键的是很多插件自己的文档也不清不楚,弄得大家都很迷茫,我的经验就是多试多测,自己多写一写,看命令行打印的错误,去找原因,不要一看到报错就慌了,很多新手最容易犯错就是一看到报错就怀疑人生了,一定要看报错记录,一般都有提示,按照提示去解决相应问题就好啦。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2016-08-04 14:08:57

前端javascripthtml

2016-11-07 21:15:12

前后端分离expressJavascript

2016-11-07 21:24:08

HtmlNode.jsJavascript

2023-12-27 14:19:33

Python内置函数开发

2012-03-28 09:40:40

JavaScript

2021-02-04 10:22:32

前端开发技术

2023-02-13 00:18:22

前端库框架集合

2018-07-20 14:30:15

2019-09-03 11:32:36

PythonLinux操作系统

2020-11-10 07:11:23

Linux内核补丁

2017-11-07 22:25:17

网站评测工具YSlow

2024-04-01 11:52:46

2020-02-25 22:00:22

机器人人工智能系统

2013-07-05 14:45:05

AndroidOpenGL ES开发

2020-05-19 10:45:31

沙箱前端原生对象

2015-03-10 11:21:44

JavaScript组JavaScript

2020-06-18 10:03:13

在家工作疫情统一通信

2021-01-26 10:33:45

前端开发技术

2018-01-10 12:09:12

Android开发程序员

2014-08-14 10:31:04

点赞
收藏

51CTO技术栈公众号