基于webpack4搭建一个react脚手架
目录
react-sample-javascriptReact 16.0 boilerplate with react-router-dom,redux & webpack 4. (for javascript) 项目初始化统一规范代码格式
:. │ .babelrc #babel的规则以及插件 │ .editorconfig #IDE/编辑器相关的配置 │ .eslintignore #Eslint忽视的目录 │ .eslintrc.js #Eslint的规则和插件 │ .gitignore #Git忽视的目录 │ .postcssrc.js #postcss的插件 │ package-lock.json │ package.json #项目相关的包 │ README.md │ yarn.lock │ ├─build #webpack相关的配置 │ utils.js #webpack配置中的通用方法 │ webpack.base.conf.js #webpack的基础配置 │ webpack.dev.conf.js #webpack的开发环境配置 │ webpack.prod.conf.js #webpack的生产环境配置 │ └─src #主目录,业务代码 │ app.css │ App.js │ favicon.ico │ index.ejs │ index.js │ └─assets #静态目录,存放静态资源 │ config.json │ └─img logo.svg 安装依赖
yarn add eslint eslint-loader eslint-config-airbnb eslint-plugin-import eslint-friendly-formatter eslint-plugin-flowtype eslint-plugin-jsx-a11y eslint-plugin-react babel-polyfill webpack jest webpack-merge copy-webpack-plugin html-webpack-plugin friendly-errors-webpack-plugin webpack-dev-server webpack-bundle-analyzer webpack-cli portfinder extract-text-webpack-plugin node-notifier optimize-css-assets-webpack-plugin autoprefixer mini-css-extract-plugin autoprefixer css-loader less-loader postcss-loader postcss-import postcss-loader style-loader babel-core babel-eslint babel-loader babel-plugin-transform-runtime babel-plugin-import babel-preset-env babel-preset-react babel-polyfill url-loader cross-env file-loader -D 项目配置webpack 基础配置
module.exports = { context: path.resolve(__dirname,‘../‘),//绝对路径。__dirname为当前目录。 //基础目录用于从配置中解析入口起点。因为webpack配置在build下,所以传入 ‘../‘ entry: { app: (‘./src/index.js‘) //项目的入口 },output: { path: path.resolve(__dirname,‘../dist‘),filename: ‘[name].[hash:8].js‘,publicPath: ‘/‘,libraryTarget: ‘umd‘,},} entryentry可以分别为字符串、数组和对象。 倘若应用只有一个单一的入口,entry的值可以使用任意类型,不会影响输出结果。 // entry为字符串 { entry: ‘./src/index.js‘,output: { path: ‘/dist‘,filename: ‘bundle.js‘ } } // 结果会生成 ‘/dist/bundle.js‘ // entry为数组,可以添加多个彼此不互相依赖的文件。结合output.library选项,如果传入数组,则只导出最后一项。 { //如果你在html文件里引入了‘bable-polyfill‘,可以通过数组将它加到bundle.js的最后。 entry: [‘./src/index.js‘,‘babel-polyfill‘],output:{ path: ‘/dist‘,filename: ‘bundle.js‘ } } // entry为对象,可以将页面配置为多页面的而不是SPA,有多个html文件。通过对象告诉webpack为每个入口,成一个bundle文件。 // 多页面的配置,可能还要借助于HtmlWebpackPlugin,来指定每个html需要引入的js { entry: { index: ‘./src/index.js‘ main: ‘./src/index.js‘ login: ‘./src/login.js‘ } output:{ path: ‘/dist/pages‘ filename: ‘[name]-[hash:5].js‘ //文件名取自‘entry‘对象的键名,为了防止推送代码后浏览器读缓存,故再生成的文件之后加上hash码。 } } // 会分别生成index.js,main.js,login.js三个文件 关于 webpack构建多页面 可以参考这篇文章。不过现在webpack4.x也是一次断崖式升级,感兴趣的同学可以自行搜索。 // entry也可以传入混合类型 { entry:{ vendor: [‘jquery‘,‘amap‘,‘babel-polyfill‘] //也可以借助CommonsChunkPlugin提取vendor的chunk。 index: ‘./src/index.js‘ } output: { path: ‘/dist‘ filename: ‘[name]-[hash:5].js‘ } }
outputoutput最基础的两个配置为
output: { path: path.resolve(__dirname, resolveresolve常用的两个配置为
resolve: { // 自动解析文件扩展名(补全文件后缀)(从左->右) // import hello from ‘./hello‘ (!hello.js? -> !hello.jsx? -> !hello.json) extensions: [‘.js‘,‘.jsx‘,‘.json‘],alias: { ‘@‘: resolve(‘src‘) } }, modulemodule的选项决定了如何处理项目中的不同类型的模块。其中常用的有
noParse: function(content) { return /jquery|lodash/.test(content); }
module: { rules: [ { test: /.(js|jsx)$/,exclude: /node_modules/,enforce: ‘pre‘,use: [{ loader: ‘babel-loader‘,{ loader: ‘eslint-loader‘,// 指定启用eslint-loader options: { formatter: require(‘eslint-friendly-formatter‘),emitWarning: false } }] },{ test: /.css$/,include: /node_modules/,use: [ MiniCssExtractPlugin.loader,‘css-loader‘,{ loader: ‘postcss-loader‘,options: { plugins: () => [autoprefixer({ browsers: ‘last 5 versions‘ })],sourceMap: false,],{ test: /.(png|jpe?g|gif|svg)(?.*)?$/,loader: ‘url-loader‘,options: { limit: 10000,name: (‘assets/img/[name].[hash:7].[ext]‘) } },{ test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,name: (‘assets/media/[name].[hash:7].[ext]‘) } },{ test: /.(woff2?|eot|ttf|otf)(?.*)?$/,name: (‘assets/fonts/[name].[hash:7].[ext]‘) } } ] } 例如上述代码,就使用 Webpack开发配置因为在webpack 4.X 中使用了流行的 ”约定大于配置“ 的做法,所以在新加入配置项
在开发时,我们往往希望能看到当前开发的页面,并且能热加载。这时,我们可以借助webpack-dev-server 这个插件,来在项目中起一个应用服务器。 // package.json "scripts": { "start": "webpack-dev-server --mode development --config build/webpack.dev.conf.js",} // 设置当前的mode为development,同样这个配置也可以写在webpack.dev.conf.js中。然后使用build目录下的webpack.dev.conf.js 来配置相关的webpack。 devServer: { clientLogLevel: ‘warning‘,historyApiFallback: true,//在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html contentBase: path.resolve(__dirname,‘../src‘),compress: true,hot: true,// 热加载 inline: true,//自动刷新 open: true,//自动打开浏览器 host: HOST||‘localhost‘,port: PORT,overlay: { warnings: false,errors: true },// 在浏览器上全屏显示编译的errors或warnings。 publicPath: ‘/‘,proxy: {},quiet: true,// necessary for FriendlyErrorsPlugin // 终端输出的只有初始启动信息。 webpack 的警告和错误是不输出到终端的 watchOptions: { poll: false } },plugins: [ new webpack.DefinePlugin({ ...process.env }),//开启HMR(热替换功能,替换更新部分,不重载页面!) new webpack.HotModuleReplacementPlugin(),// HMR shows correct file names in console on update. //显示模块相对路径 new webpack.NamedModulesPlugin(),//不显示错误信息 new webpack.NoEmitOnErrorsPlugin(),// https://github.com/ampedandwired/html-webpack-plugin ] 其实在开发时,我们可以设置 另外,当使用 history 路由时,要配置 项目进阶生产环境配置在使用webpack 4.x 的 mode 配置之后,需要我们手动配置的项已经减少了很多,像js代码压缩这种工具 代码分离// 设置代码分离的输出目录 output: { path: path.resolve(__dirname,filename: (‘js/[name].[hash:8].js‘),chunkFilename: (‘js/[name]-[id].[hash:8].js‘) },// 代码分离 optimization: { runtimeChunk: { name: "manifest" },splitChunks: { chunks: ‘all‘ } },
css代码压缩借助 我们可以新建一个util.js的文件,在webpack当中一些共用的方法。考虑使用个别配置字段 new MiniCssExtractPlugin({ filename: ‘css/[name].[hash:8].css‘,chunkFilename: ‘css/[name]-[id].[hash:8].css‘,}), 生成HTML使用 new HtmlWebpackPlugin({ filename: ‘index.html‘,template: ‘./src/index.ejs‘,// 设置目录 title: ‘React Demo‘,inject: true,// true->‘head‘ || false->‘body‘ minify: { //删除Html注释 removeComments: true,//去除空格 collapseWhitespace: true,//去除属性引号 removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference },// necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: ‘dependency‘ }), <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="robots" content="noindex,nofollow"> <title><%= htmlWebpackPlugin.options.title %></title> <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> <link rel="icon" href="/favicon.ico" type="image/x-icon"> <% for (var chunk in htmlWebpackPlugin.files.css) { %> <link rel="preload" href="<%= htmlWebpackPlugin.files.css[chunk] %>" as="style"> <% } %> <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> <link rel="preload" href="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>" as="script"> <% } %> <base href="/"> </head> <body> <div id="root"></div> </body> <style type="text/css"> body { font-family: ‘Source Sans Pro‘,‘Helvetica Neue‘,Helvetica,Arial,sans-serif; } </style> </html> 复制静态目录将所以可能被请求的静态文件,分别放在assets目录下。那么在打包后,为了保证目录能正常访问(不使用CDN等加载静态资源时),我们可以配置 new CopyWebpackPlugin([{ from: ‘./src/assets/‘,to: ‘assets‘ }]), 将 开启打包分析借助插件 // webpack.prod.conf.js const BundleAnalyzerPlugin = process.env.NODE_ENV=== ‘analysis‘ ? require(‘webpack-bundle-analyzer‘).BundleAnalyzerPlugin:null process.env.NODE_ENV=== ‘analysis‘ ? new BundleAnalyzerPlugin() : ()=>{} 在package.json 中可做如下配置: "scripts": { "analysis": "cross-env NODE_ENV=analysis webpack -p --mode production --progress --config ./build/webpack.prod.conf.js ", 通过注入环境变量,来控制是否运行打包分析。 ssh部署打包后的dist文件夹,可以直接借助 node 的 ssh-node ,直接部署到服务器指定的目录下。 ssh-node既支持ssh,也支持密码登录。建议可以为在每个项目下,新建一个.ssh文件,存放项目的私钥。代码如下: // usage: https://www.npmjs.com/package/node-ssh var path,node_ssh,ssh,fs,opn,host fs = require(‘fs‘) path = require(‘path‘) node_ssh = require(‘node-ssh‘) opn = new require(‘opn‘) ssh = new node_ssh() host = ‘localhost‘ var localDir = ‘./dist‘ var remoteDir = ‘/opt/frontend/new‘ var removeCommand = ‘rm -rf ./*‘ var pwdCommand = ‘pwd‘ ssh.connect({ host: host,username: ‘root‘,port: 22,// password,privateKey: "./.ssh/id_rsa",}) .then(function() { ssh.execCommand(removeCommand,{ cwd:remoteDir }).then(function(result) { console.log(‘STDOUT: ‘ + result.stdout) console.log(‘STDERR: ‘ + result.stderr) ssh.putDirectory(localDir,remoteDir).then(function() { console.log("The File thing is done") ssh.dispose() opn(‘http://‘+host,{app:[‘chrome‘]}) },function(error) { console.log("Something‘s wrong") console.log(error) ssh.dispose() }) }) }) 此时,在命令行直接 "scripts": { "depoly": "npm run build && node ./deploy.js",} 结语本次从零到一,新建了一个react脚手架。过程中有很多问题,也参考了不少大牛的解释。代码里也有诸多问题。还望各位看官,不吝指教。 记得留下你的足迹哦。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |