webpack打包react项目添砖加瓦

(一)首先实现编译react文件,编译并分离scss文件,静态文件的形式访问index.html

1. 各种路径信息存储在config.js文件中

2. gulp文件:引入config.js, 引入webpack.config.js, 引入gulp-webpack包

gulp default执行:

gulp.task('webpack', function() {
  gulp.src('')
    .pipe(webpack(webpackConfig()))
    .pipe(gulp.dest(config.jsDistDir));
});

gulp.task('default', ['webpack']);

3. webpack.config.js

module.exports = function() {
  return  {
    watch: true,
    entry: {
      'index/index.page': './src/js/src/page/index/index.page.jsx',
      'detail/index.page': './src/js/src/page/detail/index.page.jsx' //打包入口文件。entry对象key其实是目标路径的一部分。key也可以是绝对路径。
    },
    output: {
      path: config.jsDistDir, //目标文件地址
      filename: "[name].js" //name是entry对象的key
    },
    externals: {
      react: 'React',
      reactDOM: 'ReactDOM',
      'react-dom': 'ReactDOM' //不被打包的文件。目前react的引用方式是index.html中通过script标签引入。经实践,去掉externals对webpack打包没影响。
    },
    module: {
      loaders: [{
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loaders: ['babel?presets[]=react'] //loaders,编译react文件
      }, {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('style-loader', 
        'css!sass')
      }]
    },
    resolve: {
      alias: getAlias(),
      extensions: ["", ".min.js", ".js", ".jsx"]
    },
    plugins: [
      new ExtractTextPlugin('[name].css', {
        allChunks: true
      })
    ],
    devtool: 'source-map'
  }
}

4.可以在jsx文件中直接require.scss文件: sass-loader编译scss文件, css-loader让css文件可以作为模块引入,extractTextPlugin将css抽取问一个独立文件,可以在head中通过link方式引入

5. resolve中的alias可以为模块取别名。一些自定义的模块可以不通过文件路径而通过别名的方式来引用。getAlias是自定义函数,实现了filename-filePath的map

(二)加入express

1. 设置serve任务

gulp.task('serve', function() {
  var app = express();
  app.use(appConfig.jsPath, headerStatic(path.join(appConfig.webapp, appConfig.jsPath), {})); //js路径映射
  app.use(appConfig.cssPath, headerStatic(path.join(appConfig.webapp, appConfig.cssPath), {}));//css路径映射
  app.use("", headerStatic(appConfig.webapp, {}));//html, 放在webapp目录下
  gulp.run('webpack');//打包
  app.listen(8082, function(err) { //监听请求
    if (err) { return console.log(err); }
    console.log('listening on %d', 8082);
  });
});

2. headerStatic函数返回一个函数,这个函数根据请求req返回静态文件。

function headerStatic(staticPath, headers) {
  return function(req, res, next) {
    var reqPath = req.path === '/' ? 'index' : req.path;
    var f = path.join(staticPath, reqPath);
    //设置不同的contentType
    if (fs.existsSync(f)) {
      if (/\.html$/.test(reqPath)) {
        res.set('Content-Type', 'text/html');
        res.send(fs.readFileSync(f, 'utf-8'));
      } else {
        if (/\.js$/.test(reqPath)) {
          res.set('Content-Type', 'text/javascript');
          res.send(fs.readFileSync(f, 'UTF-8'));
        } else if (/\.css$/.test(reqPath)) {
          res.set('Content-Type', 'text/css');
          res.send(fs.readFileSync(f, 'UTF-8'));
        } else {
          res.send(fs.readFileSync(f));
        }
      }
    } else {
      next();
    }
  } 
}

(三)build  

 1. 在这个demo中build的过程很简单,包括:clean, webpack, uglifyjs, minifyCss, revision-css, revision-js, revreplace-html。

 2. 这几个步骤分别如下实现:

clean: 清空上次打包生成的文件

gulp.task('clean', function() {
  return gulp.src([tmp1, tmp2, appConfig.build], {read: false}).pipe(clean()); //tmp1, tmp2是生成目标文件的中间目录,appConfig.build是存放最终打包好的文件的位置。每个项目是否需要中间目录及中间目录的路径是自定义的
});

webpack: 编译js文件。在我们的demo中,打包后的文件在目录/.tmp/step1/js/dist下

uglifyjs: 压缩js文件。压缩后的文件仍在tmp1下

gulp.task('uglifyjs', function() {
  return gulp.src(path.join(tmp1, '**/*.js'))
              .pipe(gulpif(uglifyJs, uglify().on('error', util.log))) //gulpif: 第一个参数是函数。如果返回true, 执行后面的语句
              .pipe(gulp.dest(tmp1));
});

minifyCss: 压缩css文件

压缩后的css文件也在tmp1下。

gulp.task('minifyCss', function() {
  return gulp.src(path.join(tmp1, '**/*.css'))         
         .pipe(minifyCss())
         .pipe(gulp.dest(tmp1));
});

revision-css, revision-js: 为js和css文件追加hash后缀标识版本

gulp.task('revision-css', function() {
  return gulp.src(path.join(tmp1, '**/*.css'))
         .pipe(rev())
         .pipe(gulp.dest(tmp2))  //加过版本号的css文件拷贝到tmp2目录下
         .pipe(rev.manifest('style-rev-manifest.json')) //生成原有文件名与revision后的文件名的map
         .pipe(gulp.dest(tmp2));        
});

gulp.task('revision-js', function() {
  return gulp.src(path.join(tmp1, '**/*.js'))
          .pipe(rev())
          .pipe(gulp.dest(tmp2))
          .pipe(rev.manifest('js-rev-manifest.json'))
          .pipe(gulp.dest(tmp2));  
});

 revreplace-html: 把html文件中对js和css文件的引用替换为最新版本。输出目标为tmp2

gulp.task('revreplace-html', function() {
  var manifest = gulp.src([
    path.join(tmp2, '/style-rev-manifest.json'),
    path.join(tmp2, '/js-rev-manifest.json')
  ]);

  return gulp.src(path.join(appConfig.webapp, '**/*.html'))
        .pipe(revReplace({
          manifest: manifest,
          replaceInExtensions: ['.html']
        }))
        .pipe(gulp.dest(tmp2));
});

(四)livereload: 文件更新后自动刷新页面

function watchFiles(ext) {
  gulp.watch(path.join(config.webappDir, '**/*.' + ext), function(event) {
    tinylr.changed(event.path);
  });
}

//gulp task serve中
app.use(body()).use(tinylr.middleware({ //tinylr = require('tiny-lr');
    app: app
}));

watchFiles('js');
watchFiles('html');

  

以上,这个简单的demo可以通过gulp serve启动本地调试环境,通过gulp build构建生产环境的文件

posted @ 2017-08-16 20:56  Angela多多  阅读(344)  评论(0编辑  收藏  举报