Source: http://www.jianshu.com/p/9724c47b406c
Why is front-end engineering required?
The significance of front-end engineering is to make the front-end of the industry from the savage era to the regular Army era, many related tools and concepts were born in recent years. Curiosity Daily in the process of front-end engineering, the main challenge is to solve the following problems:
? How do I manage the front-end code for multiple projects?
? How do I synchronize the reuse code?
? How to make the development experience more refreshing?
Too many projects
Have you ever written a blog post on how to manage generic projects that are referenced by multiple projects? , the article mentions that the Curiosity daily project is too much (Pc/mobile/app/pad), to develop front-end components for so many projects and maintenance is a tedious task, and there will be a lot of redundant work.
Better management of front-end code
Front-end code to adapt to the background directory specification, can be very good front-end directory structure is torn apart, front-end code is decentralized and not easy to manage, and the development experience is very unfriendly.
With the concept of front-end engineering, front-end projects and background projects can be completely separated, front-end according to their own directory structure to organize the code, and then in a certain way to build output to the background project, simply Perfect (is not a kind of harem 3,000 feeling).
Technology selection
Research the market mainstream building tools, including Gulp, Webpack, FIS, and finally decided around gulp to create a front-end engineering solution, while introducing webpack to manage modular code, the general Division of Labor as follows:
gulp
: Handles HTML compression/preprocessing/conditional compilation, image compression, auto-merging of sprites and other tasks
webpack
: Manage modularity and build js/css.
The main reason for choosing Gulp & Webpack is that gulp is relatively flexible, can do more customization tasks, and webpack is too good in modular schemes (can't help but praise).
How to design the front-end project directory structure?
Pull out of the front-end project directory structure as follows
Front-end Project structure
appfe目录
: APPFE is the front-end project mentioned earlier, this project mainly consists of two parts: front-end code, build task
appfe > gulp目录
: Contains all the Gulp subtasks, each of which contains all the logic for the related task.
appfe > src目录
: Contains all the front-end code, such as pages, components, pictures, font files, and so on.
appfe > package.json
: It's needless to say.
appfe > gulpfile.js
: Gulp entry file, introducing all the gulp subtasks.
The ideal is very plump, the reality is very bony, so beautiful desire, in the concrete practice process, doomed to spend a lot of thought, to tread a lot of pits.
Curiosity daily This upgrade will be on the line, and finally have time to integrate the previous bits and pieces of the blog, and combined with their own experience to share to everyone, of course, the future may also have a larger adjustment, here, we can refer to ideas.
What is Gulp?
Gulp is a flow-based build tool that is simpler and more efficient than other component tools.
Tip: Before writing a gulp primer, you can refer to the following, if you have a certain understanding of gulp please skip directly.
What is Webpack?
Webpack is a modular management tool that uses Webpack to enable module-on-demand loading, module preprocessing, module packaging, and other functions.
Tip: Before writing a webpack primer, you can refer to the following, if you have a certain understanding of Webpack please skip directly.
How to integrate Gulp & Webpack
Webpack is a complicated part of many gulp sub-tasks, mainly related to Js/css.
Including: module analysis, on-demand loading, JS code compression Merge, extraction from public modules, Sourcemap, POSTCSS, CSS code compression, etc...
Webpack-stream solution [Not recommended]
Using Webpack-stream Although it is convenient to integrate Webpack into gulp, there are fatal problems:
If you turn off the Webpack listening mode, it is time-consuming to compile the Js/css file every time the file changes.
If you turn on Webpack listening mode, other gulp tasks will be blocked, causing the listener of other gulp tasks to fail.
So this scheme is almost unusable!
Webpack Native Program
Direct use of the Webpack native scheme is relatively more flexible.
Tip: The code is more complex, it involves a lot of knowledge, it is recommended to look at the shape of the good, if really interested, can study hard, after all, spent a long time to think about these programs.
Webpack.config.js key places have roughly commented var _ = require (' Lodash '); var path = require (' path '); var webpack = require (' Webpack '); var E Xtracttextplugin = require ("Extract-text-webpack-plugin"); var autoprefixer = require (' autoprefixer '); var flexibility = Require (' postcss-flexibility '); var sorting = require (' postcss-sorting '); var color_rgba_fallback = require (' Postcss-color-rgba-fallback ') var opacity = require (' postcss-opacity '); var pseudoelements = require (' Postcss-pseudoelements '); var will_change = require (' Postcss-will-change '); var Cssnano = require (' Cssnano '); var Project = require ('./lib/project ') (); var config = require ('./config. ' + project). webpack;//Loaders configuration var getloaders = Fun Ction (env) {return [{test:/\.jsx?$/, exclude:/(Node_modules|bower_components|vendor)/, Loader: ' Babel?presets[]=es2015&cachedirectory=true!preprocess? project= ' + project}, {test:/\.css$/, Loader:ExtractTextPlugin.extract ("Style-loader", "Css-loader!po Stcss-loader ") }, {test:/\.less$/, Loader:ExtractTextPlugin.extract ("Style-loader", "css-loader!postcss-loader!less-l Oader ")}, {test:/\/jquery\.js$/, Loader: ' Expose?$!expose?jquery!expose?jquery '}, {test:/ \.xtpl$/, loader: ' Xtpl '}, {test:/\.modernizrrc$/, Loader: "Modernizr"}];};/ /alias configuration var getalias = function (env) {return {///special ' jquery ': Path.resolve (__dirname, ' ... /src/vendor/jquery2/jquery.js '),//Normal third party library ' jquery.js ': Path.resolve (__dirname, '. /src/vendor/jquery2/jquery.js '),};};/ /Plugin configuration var getplugins = function (env) {var defaultplugins = [//This is not only an alias, but also a module new webpack can be introduced automatically when an alias is encountered. Provideplugin ({' $ ': ' jquery.js ', ' jquery ': ' jquery.js ', ' jquery ': ' Jquery.js ',}) ,//Extraction from public module new Webpack.optimize.CommonsChunkPlugin (' Common ', ' Common.js '), New Extracttextplugin ( Path.join ('.. /.. /stylesheets ', project, '/[name].css '), {allchunks:true}); if (env = = ' production ') {//on-line mode configuration, remove dependencies in duplicate plugins/compression js/exclude error Plugins plugins = _.union (Defaultplugins, [ New Webpack.optimize.DedupePlugin (), new Webpack.optimize.UglifyJsPlugin ({sourcemap:false, Mangle: {except: [' $ ', ' JQuery '}}], new WEBPACK.N Oerrorsplugin ()]); } else {plugins = _.union (Defaultplugins, []); } return plugins;};/ /POSTCSS configuration var getpostcss = function (env) {var postcss = [Autoprefixer ({browers: [' Last 2 versions ', ' IE > = 9 ', ' > 5% in CN ']}), flexibility, Will_change, color_rgba_fallback, opacity, Pseu Doelements, sorting]; if (env = = ' production ') {//on-line mode configuration, CSS compression return function () {return _.union ([C Ssnano ({//OffCssnano autoprefixer option, otherwise it will conflict with the previous Autoprefixer Autoprefixer:false, Reduceidents:fal SE, Zindex:false, Discardunused:false, Mergeidents:false })], postcss); }; } else {return function () {return _.union ([], postcss); }}};//as a function export configuration, the code is more concise module.exports = function (env) {return {context:config.context, entry:config . SRC, Output: {Path:path.join (Config.jsdest, Project), filename: ' [name].js ', ch Unkfilename: ' [name]. [Chunkhash:8].js ', Publicpath: '/assets/' + project + '/'}, Devtool: "eval", Watch:false , Profile:true, Cache:true, module: {loaders:getloaders (env)}, Resolve: {Alias:getalias (env)}, Plugins:getplugins (env), POSTCSS:GETPOSTCSS (ENV)};}
Webpack task var _ = require (' Lodash '); var del = require (' del '); var webpack = require (' Webpack '); var gulp = require (' Gulp ') var plumber = require (' Gulp-plumber '), var newer = require (' gulp-newer '); var logger = require (' Gulp-logger '); var project = require ('.. /lib/project '); var config = require ('.. /config. ' + project '. Webpack;var Compilelogger = require ('.. /lib/compilelogger '); var handleerrors = require ('.. /lib/handleerrors ');//Generate Js/cssgulp.task (' Webpack ', [' clean:webpack '], function (callback) {Webpack (Require (') '. /webpack.config.js ') (), function (err, stats) {Compilelogger (err, stats); Callback (); });});/ /Generate js/css-Listening mode gulp.task (' Watch:webpack ', [' clean:webpack '], function () {Webpack (_.merge (Require (') '. /webpack.config.js ') () (), {watch:true}). Watch (The function (err, stats) {Compilelogger (err, stats); });});/ /Generate Js/css-build Mode Gulp.task (' Build:webpack ', [' clean:webpack '], function (callback) {Webpack (_.merge (Require (') '. /webpack.config.js '(' production '), {devtool:null}), function (err, stats) {Compilelogger (err, stats); Callback (); });});/ /Cleanup Js/cssgulp.task (' Clean:webpack ', function () {return del ([Config.jsdest, Config.cssdest], {FORC E:true});
What are the holes in practice? How do I organize gulp tasks?
Because gulp tasks are many, and each core task has associated tasks, such as Webpack's associated tasks have webpack
/// watch:webpack
build:webpack
clean:webpack
, how to organize these subtasks is a need to be careful, out of habit: To put the logic of the Association together, So my solution is to webpack related tasks into a file and then define/// default
/ clean
watch
build
Four portal tasks to reference the corresponding subtasks.
Webpack task Structure Gulp How to implement error self-start
Use watch mode can be more efficient development, monitor the change to automatically perform the task, but if the process encountered an error, Gulp will error and stop watch mode, must restart Gulp, simply God annoying!
The gulp-plumber can be used to implement error self-booting, so you can happily develop in watch mode without worrying about error.
Further combined with gulp-notify, can be notified when an error occurs, to facilitate the detection of problems.
// 错误处理var notify = require("gulp-notify")module.exports = function(errorObject, callback) { // 错误通知 notify.onError(errorObject.toString().split(‘: ‘).join(‘:\n‘)) .apply(this, arguments); // Keep gulp from hanging on this task if (typeof this.emit === ‘function‘) { this.emit(‘end‘); }}// 任务var gulp = require(‘gulp‘);var plumber = require(‘gulp-plumber‘);var project = require(‘../lib/project‘)(); // 得到当前的后台项目var config = require(‘../config.‘ + project).views; // 读取配置文件var handleErrors = require(‘../lib/handleErrors‘);gulp.task(‘views‘, function() { return gulp.src(config.src) .pipe(plumber(handleErrors)) // 错误自启动 .pipe(gulp.dest(config.dest));});
Gulp How to handle synchronization tasks and asynchronous tasks
同步任务
: Gulp return stream
the way to end the current task and stream
transfer it to the next task, most gulp tasks are in synchronous mode.
异步任务
: In the actual project, some of the task's logic is executed by the asynchronous function, the return timing of such a task is not accurate control, usually need to call in the asynchronous function callback()
to tell gulp the end of the task, and this is callback
nothing, is to upload a parameter in the task, no practical significance.
// 同步任务gulp.task(‘views‘, function() { return gulp.src(config.src) .pipe(plumber(handleErrors)) .pipe(gulp.dest(config.dest));});// 异步任务gulp.task(‘webpack‘, function(callback) { webpack(config, function(err, stats) { compileLogger(err, stats); callback(); //异步任务的关键之处,如果没有这行,任务会一直阻塞 });});
Webpack How to extract a standalone CSS file
Webpack the default is to inject CSS directly into the HTML, this method is not universal, not recommended.
In combination extract-text-webpack-plugin
, you can generate a standalone CSS file that extract-text-webpack-plugin
parses each one require(‘*.css‘)
and then processes the output of a separate CSS file.
// webpack.config.jsvar ExtractTextPlugin = require("extract-text-webpack-plugin");module.exports = { entry: { ‘homes/index‘: ‘pages/homes/index.js‘ }, output: { filename: "[name].js" }, module: { loaders: [{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }] }, plugins: [ new ExtractTextPlugin("[name].css") ]}
Webpack How to extract common logic and style
Before Webpack, want to pull out of the public module completely need manual maintenance, because JS is a dynamic language, all dependencies are run to determine, Webpack can do static parsing, analysis of the dependencies between files, use CommonsChunkPlugin
can automatically pull out of the public module.
// webpack.config.jsvar webpack = require(‘webpack‘);var ExtractTextPlugin = require("extract-text-webpack-plugin");module.exports = { entry: { ‘homes/index‘: ‘pages/homes/index.js‘ }, output: { filename: "[name].js" }, module: { loaders: [{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }] }, plugins: [ //抽离公共模块,包含js和css new webpack.optimize.CommonsChunkPlugin("commons", "commons.js"), new ExtractTextPlugin("[name].css") ]}
Webpack's Watch mode
Webpack relatively time-consuming, especially when the project is more complex, need to parse more files. Curiosity daily Web Project The first full-scale execution of the Webpack task takes approximately 10s, so an incremental build must be introduced. Incremental builds simply need to add the watch parameter to the Webpack configuration.
Webpack Task Output Log
The problem, however, is that if you add the watch parameter to Webpack-stream, the Webpack-stream task will block the other watch tasks and eventually cause the incremental build of other tasks to fail.
So if you want to use Webpack's incremental build, you need to use the native Webpack scheme!
Flexible Webpack Entry File
The Webpack portal file receives three formats: strings, arrays, objects, and for multi-page scenarios, only objects can satisfy the criteria, so we list all the entry files.
However, this kind of scheme is extremely inflexible, can you read all the entry files under a file for reference to the gulp scheme? To solve this problem, a function has been customized to implement this function.
//获取文件夹下面的所有的文件(包括子文件夹)var path = require(‘path‘), glob = require(‘glob‘);module.exports = function(dir, ext) { var files = glob.sync(dir + ‘/**/*.‘ + ext), res = {}; files.forEach(function(file) { var relativePath = path.relative(dir, file), relativeName = relativePath.slice(0, relativePath.lastIndexOf(‘.‘)); res[relativeName] = ‘./‘ + relativePath; }); return res;};
Webpack development/production Configuration Merge
The development configuration and production configuration of the Webpack task are very different, and each has its own configuration.
Because the webpack.config.js
default notation is to return an object, the object does not have different output depending on the conditions, so it will be changed to a webpack.config.js
function to implement different outputs by passing in parameters.
// 其中定义了getLoaders,getAlias,getPlugins,getPostcss函数// 都是为了解决development配置和production配置的差异问题// 既最大程度的复用配置,又允许差异的存在module.exports = function(env) { return { context: config.context, entry: config.src, output: { path: path.join(config.jsDest, project), filename: ‘[name].js‘, chunkFilename: ‘[name].[chunkhash:8].js‘, publicPath: ‘/assets/‘ + project + ‘/‘ }, devtool: "eval", watch: false, profile: true, cache: true, module: { loaders: getLoaders(env) }, resolve: { alias: getAlias(env) }, plugins: getPlugins(env), postcss: getPostcss(env) };}
Webpack How to load JS file asynchronously on line mode
Webpack can be the JS code shard, the import file depends on all the modules packaged into a file, but some scenarios of JS code does not need to be packaged into the portal file, more suitable for asynchronous lazy loading, so as to maximize the first screen loading speed.
For example, Curiosity Daily's login floating layer, which contains complex image upload, picture clipping, frame logic, but it is not necessary to package in the import file, but it is very suitable for asynchronous lazy loading, only when the need to login/register to request.
Image upload cropping
We can implement asynchronous loading via Webpack, and require
require.ensure
it is worth mentioning that, in addition to the list of asynchronous load files specified, Webpack automatically resolves the dependency of the callback function and the deep dependency of the specified list and packages it into a file.
But the actual project also has to solve the problem of browser caching, because these asynchronous JS file timestamp is produced by rails, for Webpack is not known, that is, the request of this asynchronous JS file is not hit.
To solve this problem, we have customized a rake task in RAILS4: to produce an asynchronous JS file without a timestamp version.
Rake Task
There is also a small detail that these asynchronous JS files have two timestamp, the former is the Webpack timestamp, the latter is the rails timestamp, the reason is two timestamp, is to solve the problem of browser caching.
In short, it is:
Through require
/ require.ensure
, to generate asynchronous JS file, solve the problem of asynchronous loading.
By customizing the Rake task, the asynchronous JS file without rails Timestamp is generated to solve the problem that Webpack does not recognize rails timestamps.
Through the Chunkfilename configuration of Webpack, add webpack timestamp to the asynchronous JS file to solve the problem of browser cache.
What do you say?
Front-end engineering can automate a number of complex tasks, improve development efficiency, and reduce low-level errors.
More importantly, the beginning of the article, the most important point of the front-end engineering is to give us a new perspective to look at the front-end development, so that the front-end development can do more complex, more challenging things!
This is the first post in the engineering practice of the front end, followed by a summary of the previous fragmented blog post. Overall, however, the Webpack task is one of the most complex and knowledgeable tasks.
If there are any flaws in the article, please correct me.
Gulp & Webpack Integration, fish and bear's paw I want!