Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[React项目总结] 基于 webpack 搭建前端工程基础篇 #1

Open
chenbin92 opened this issue Jul 12, 2016 · 6 comments
Open

[React项目总结] 基于 webpack 搭建前端工程基础篇 #1

chenbin92 opened this issue Jul 12, 2016 · 6 comments

Comments

@chenbin92
Copy link
Owner

chenbin92 commented Jul 12, 2016

目录

  1. 技术选型
  2. webpack 基础
    • 安装 webpack
    • webpack 简介
    • 使用webpack-dev-server启动服务器
  3. 配置 React, ES6 & Babel 6
  4. 使用 ESlint 进行代码检查
  5. 样式处理
  6. 图片处理
  7. 区分开发及生产环境
  8. 构建流程图
  9. 文件说明
  10. 参考

1. 技术选型

最近在一个项目初步尝试使用了reactJS,开发周期两周,选用的技术栈大致如下:

JavaScript:

  • Language: ES6
  • Framework: React, Redux

CSS:

  • Language: SCSS
  • Framework: ant-design

Build Tool:

  • Webpack

Dependency manage

  • npm

Git workflow:

  • Gitlab flow

API:

  • JSON

2. webpack 基础

2.1安装 webpack

作为全局变量安装

 $ npm install -g webpack
 $ webpack -v => webpack 1.13.0

作为项目依赖安装

$ npm install webpack --save-dev

2.2 webpack 简介

webpack 的配置项主要包括以下几点:

webpack_config_js.png

  • entry: 入口,定义要打包的文件
  • output: 出口,定义打包输出的文件;包括路径,文件名,还可能有运行时的访问路径(publicPath)参数
  • module: webpack将所有的资源都看做是模块,而模块就需要加载器;主要定义一些loaders,定义哪些后缀名的文件应该用哪些loader
    • test: 检测哪些文件需要此loader,是一个正则表达式
    • exclude: 忽略哪些文件
  • resolve: 定义能够被打包的文件,文件后缀名
  • plugins: 定义一些额外的插件
示例用到的 Loaders
  • 处理样式:sass-loader、style-loader、css-loader,将 sass 转成 css
  • 图片处理,url-loader、file-loader、image-webpack-loader, 将图片转换成base64 或者 进行压缩
  • js处理: babel-loader,babel-preset-es2015,babel-preset-react,将es6或更高级的代码转成es5的代码
示例用到的 Plugins
  • 代码热替换:HotModuleReplacementPlugin
  • 生成html文件:HtmlWebpackPlugin
  • 报错但不退出webpack进程:NoErrorsPlugin
  • 代码压缩:UglifyJsPlugin
  • 自动打开浏览器: OpenBrowserPlugin
  • 设置环境变量: DefinePlugin

2.3 使用 webpack-dev-server 启动服务器

2.3.1 刷新功能

webpack提供的一个静态资源服务器,这个家伙可不太好配置,看官方文档给看懵了,试验了好几次才配成功,后面我们会根据 NODE_ENV 环境变量分别配置 dev mode server 和 prod mode serve;先来看看其功能:

  • 支持两种模式的自动刷新(Automatic Refresh)

    • iframe模式
    • inline模式
  • 支持热更新(Hot Module Replacement)

    注: 自动刷新和热更新是两个概念

2.3.2 这里主要介绍自动刷新 inline 模式的配置,inline模式又分为两种配置:
  • 命令行模式: 命令行模式只需要加上 --line选项即可

    $ webpack-dev-server --line
    
  • Node.js API: 使用node.js api需要手动添加配置

    var config = {
      entry: [
        'webpack/hot/dev-server',
        'webpack-dev-server/client?http://localhost:8080',
        path.resolve(__dirname, 'app/index.js')
      ],
    }
    
2.3.3 配置 Hot Module Replacement

这是webpack最牛逼的特性之一,即模块热替换,在前端代码变动的时候无需整个刷新页面,只把变化的部分替换掉。使用HMR功能也有两种配置方式:

  • 命令行方式: 命令行模式只需要加上 --line --hot 选项。 --hot 会自动把 webpack/hot/dev-server 加入到了webpack配置文件中的入口点。如果执行正确,可以在浏览器的控制台看到以下信息。

    [HMR] Waiting for update signal from WDS...
    [WDS] Hot Module Replacement enabled.
    
  • Node.js API: 手动配置需要做三件事情:

    • webpack/hot/dev-server 加入到webpack配置文件的entry项
    • new webpack.HotModuleReplacementPlugin() 加入到webpack配置文件的plugins项
    • hot:true 加入到webpack-dev-server的配置项里面
2.3.4 完整配置,主要分为三部分:
  • webpack.config.js: webpack常规配置,配置入口文件,输出文件,loaders等等

  • server.js: 将server部分分离到一个单独到的文件配置

  • package.json: 自定义启动命令

    <!-- webpack.config.js -->
    var webpack = require('webpack');
    var path = require('path');
    var config = {
      entry: [
        path.resolve(__dirname, 'app/index.js')       // 定义入口文件
      ],
      output: {                                       // 定义出口目录
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js',
        publicPath: '/'
      },
      resolve: {                                      // resolve 指定可以被 import 的文件后缀
        extensions: ['', '.js', '.jsx']
      },
      module: {
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin()
      ]
    }
    
    module.exports = config;
    
    
    <!-- server.js -->
    var webpack = require('webpack');
    var webpackDevServer = require('webpack-dev-server');
    var config = require('./webpack.config.js');
    
    var compiler = webpack(config);
    var server = new webpackDevServer(compiler, {
      historyApiFallback: true,
      hot: true,
      inline: true,
      progress: true,
      contentBase: './app',
      stats: { colors: true }
    });
    
    config.entry.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server");
    
    server.listen(8080, "localhost", function(err) {
      if(err) {
        console.log(err);
      }
      console.log('Listening at localhost:8080...');
    });
    
    
    <!-- package.json -->
    "scripts": {
      "start": "node server.js"
    }
    

    现在你可以通过运行 npm start 启动服务器来。

hrm


3. 配置 React, ES6 & Babel 6

ES6 和 JSX 转换

在最新的babel 6.x 版本中,在指定哪些代码转换器将被启用时,需要配置 presets 。最简单配置的方法就是项目根目录下建一个 .babelrc 文件:

  // .babelrc
  {
    "presets": [
      "es2015",
      "react"
    ]
 }

配置好以上代码,你就可以开始用 es2015, jsx 了,相关的依赖包

  * babel-loader: 转换JSX
  * babel-core: 即babel的包
  * babel-preset-es2015: es2015的babel预设

4. 使用 ESlint 进行代码检查

特点:

  • 默认规则包含所有 JSLint、JSHint 中存在的规则,易迁移
  • 规则可配置性高:可设置「警告」、「错误」两个 error 等级,或者直接禁用;
  • ESLint 支持 JSX, 不过目前为 alpha 版本,正式版发布之前可以先使用 eslint-plugin-react 替代

配置:

可以通过以下三种方式配置 ESLint:

  • 使用 .eslintrc 文件(支持 JSON 和 YAML 两种语法)
  • package.json 中添加 eslintConfig 配置块
  • 直接在代码文件中定义

下面主要演示通过 .eslintrc 配置的步骤:

4.1 在项目根目录下面新建 .eslintrc 文件
$ touch .eslintrc 
4.2 Specifying Parser Options
{
  "parserOptions": {
    "ecmaVersion": 6,  // 指定ECMAScript 版本
    "sourceType": "module", // 设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)
    "ecmaFeatures": { 
        "jsx": true // 启用 JSX
    }
  }
}
4.3 Specifying Environments
{
  "env": {
    "browser": true,
    "node": true,
    "es6": true,
    "mocha": true
  }
}
4.4 Configuring Plugins
"plugins": [
  "plugin1",
  "eslint-plugin-plugin2"
]
4.5 Configuring Rules
{
  "rules": {
      "eqeqeq": "off",
      "curly": "error",
      "quotes": ["error", "double"]
  }
}
4.6 Extending Configuration Files
  • extends 属性值可以是:

    • 在配置中指定的一个字符串
    • 字符串数组:每个配置继承它前面的配置

    *rules 属性可以做下面的任何事情以扩展(或覆盖)规则:
    参数:0 关闭,1 警告,2 错误

    • 启用额外的规则
    • 覆盖基础配置中的规则的默认选项
    • 禁用基础配置中的规则
4.7 以下是eslint配置文件常见的格式
{
  "parserOptions": { //EsLint通过parserOptions,允许指定校验的ecma的版本,及ecma的一些特性
    "ecmaVersion": 6, //指定ECMAScript支持的版本,6为ES6
    "sourceType": "module", //指定来源的类型,有两种”script”或”module”
    "ecmaFeatures": { // ecmaFeatures指定你想使用哪些额外的语言特性
        "jsx": true //启动JSX
    }
  },
  "parser": "babel-eslint", // EsLint默认使用esprima做脚本解析,也可以切换成babel-eslint解析
  "env": { // Environment可以预设好的其他环境的全局变量,如brower、node环境变量、es6环境变量、mocha环境变量等
    "browser": true,
    "node": true,
    "es6": true,
    "mocha": true
  },
  "plugins": [ // EsLint允许使用第三方插件
    "react"
  ],
  extends: [ // Extends是EsLint默认推荐的验证你可以使用配置选择哪些校验是你所需要的
    "eslint:recommended"
  ],
  rules: [ // 自定义规则
  ],
  "globals": { // 即插件在执行过程中用到的其它全局变量
  }
}
4.8 Ignoring Files and Directories

通过在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录。

4.9 在Sublime中安装插件:
SublimeLinter
SublimeLinter-contrib-eslint
4.10 相关插件
  • babel-eslint: ESLint 是前端JS代码检测利器。而 babel-eslint 则允许你检测所有的 Babel 代码
  • eslint: JavaScript 语法检测利器:分析出你代码潜在的错误和非标准用法
  • eslint-plugin-react: ESLint 中关于 React 语法检测的插件

4.11工作流集成

  • 编辑器
  • 构建工具

4.12 更多参考

eslint 配置参考
Lint Like It’s 2015

4.13 运行 "eslint app

eslint


5. 样式处理

主要使用sass预处理器编写样式,需要先通过sass-loader处理成css,然后再通过css-loader加载成css模块,最后由style-loader加载器对其做最后的处理,从而运行时可以通过style标签将其应用到最终的浏览器环境。

 $ npm install css-loader style-loader sass-loader node-sass --save-dev

安装好依赖后,通过以下简单的配置就可以使用sass;这里需要注意的一点是需要开启 sourceMap 功能,便于调试。

 devtool: "source-map",
 module: {
  loaders: [
    {
      test: /(\.css|\.scss)$/,
      loaders: ["style", "css?sourceMap", "sass?sourceMap"]
    }
  ]
}

6. 图片处理

图片处理常见的loader有以下三种:

  • file-loader: 默认情况下会根据图片生成对应的 MD5 hash 的文件格式
  • url-loader: url-loader类似于file-loader,但是url-loader可以根据自定义文件大小或者转化为 base64 格式的 dataUrl,或者单独作为文件,也可以自定义对应的 hash 文件名
  • image-webpack-loader: 提供压缩图片的功能
// url-loader 配置
module: {
  loaders: [
    {
      test: /\.(jpe?g|png|gif|svg)$/i,
      loaders: [
        'url?limit=10000&name=img/[hash:8].[name].[ext]', // 图片小于8k就转化为 base64, 或者单独作为文件
        'image-webpack' // 图片压缩
      ]
    }
  ]
}
// file-loader 配置
module: {
  loaders: [
    test: /\.(jpe?g|png|gif|svg)$/i,
    loaders: [
      'file?hash=sha512&digest=hex&name=[hash].[ext]', // 生成 md5 hash 格式的文件名
      'image-webpack' // 图片压缩
    ]
  ]
}

对于小质量的图片资源,可以由 url-loader 实现将其进行统一打包,代码中 url-loader?limit=8192 的含义就是对于所有小于 8kb 的图片资源转换成base64 格式。这在一定程度上可以替代CSS Sprites方案,用于减少对于小图片资源的HTTP请求数量。


7. 配置生产环境

7.1 前端开发环境通常分为两种:
  • 开发环境: 需要日志输出,sourcemap ,错误报告等等
  • 生产环境:需要做代码压缩,对文件名进行 hash 处理等等

为了区分我们可以创建两个文件分别进行不同环境下的配置:

  • webpack.config.dev.js // 开发环境
  • webpack.config.prod.js // 生产环境
7.2 区分环境

webpack 提供了 DefinePlugin 设置环境变量,后面会根据设置的不同环境变量决定是否打包压缩,还是启动dev server 或者是 prod server

plugins: [
    ...
    new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify('production') // or development
    }),
]

判断当前环境是否是生产环境

var isProduction = function () {
  return process.env.NODE_ENV === 'production';
};

set node env

7.3 代码压缩

webPack 提供了内建插件,直接配置以下代码即可压缩代码

new webpack.optimize.UglifyJsPlugin({
  compress: {
    warnings: false
  }
})
7.4 添加 Hash 缓存
  • 对于没有修改的文件,从缓存中获取文件
  • 对于已经修改的文件,不要从缓存中获取
output: {
    ...
    filename: '[chunkhash:8].bundle.js' // chunkhash 默认是16位,可自定义配置
    ...
  },
7.5 自动生成页面

文件名带上 hash 值后,这个值在每次编译的时候都会发生变化,都需要在 html 文件里手动修改引用的文件名,这种重复工作很琐碎且容易出错,这里我们可以使用 html-webpack-plugin 来帮我们自动处理这件事情, 用来简化创建服务于 webpack bundle 的 HTML 文件,流程如下:

// 在 app 目录下建一个 index.tpl.html 作为钩子
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
// 在 webpack.config.dev.js 和 webpack.config.prod.js 添加配置代码,即可生成相对应的 index.html
plugins: [
  new HtmlWebpackPlugin({
    template: 'app/index.tpl.html',
    inject: 'body',
    filename: 'index.html'
  })
]

8 构建流程图

最终实现的构建流程如下图所示:

webpack-build-process

Configuration tasks

  • npm start: 启动开发模式下的server
  • npm run start:prod: 启动生产模式的server
  • npm run build: 打包生产模式的代码
  • npm run lint: eslint 代码检查
  • npm run lint:watch: eslint 监视
  • npm run remove:build: 删除dist目录
  • npm run clean:build: 清除dist目录

9. 文件说明

  • .babelrc: 配置 es2015,react 解析器
  • .eslintrc: 配置 eslint 代码检查
  • server.js: 配置本地 server (包含 dev server 和 prod server )
  • webpack.config.dev.js: 开发模式相关配置
  • webpack.config.prod.js: 生产模式相关配置

10. 参考:

@liwenlong
Copy link

反馈一个错误,config.entry.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server"); 这个应该放在 var compiler = webpack(config); 语句执行之前,不然实时修改刷新不好使。其实原理就是:配置项中的inline:true 无法起作用,所以需要单独额外配置。

[There is no inline: true flag in the webpack-dev-server configuration, because the webpack-dev-server module has no access to the webpack configuration. Instead, the user must add the webpack-dev-server client entry point to the webpack configuration.](https://webpack.github.io/docs/webpack-dev-server.html)

@shenyong2016
Copy link

跪求搭建react的环境和项目的详细步骤

@suxiaoX
Copy link

suxiaoX commented Jun 7, 2017

有源码吗?

@liangzhm
Copy link

liangzhm commented Jun 8, 2017

运行项目时,export不是内部或外部命令也不是可运行的程序或批处理文件,这是怎么回事

@JustinYi922
Copy link

能有个阿里云的实际例子就好了。前面说的这些都知道。只是在开始上线部署的时候,还是有点懵逼。比如如何发布,如何每次根据版本号升级系统等。

@hubcarl
Copy link

hubcarl commented Oct 26, 2017

@suxiaoX @liangzhm 服务端渲染:https://github.com/hubcarl/egg-react-webpack-boilerplate

前端渲染,可以通过 https://github.com/hubcarl/easywebpack-cli 工具初始化

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants