UNPKG

webpack-papa-script

Version:
502 lines (366 loc) 21.9 kB
# webpack-papa-script [![Build Status](https://travis-ci.org/nonjene/webpack-papa-script.svg?branch=master)](https://travis-ci.org/nonjene/webpack-papa-script) [![Coverage Status](https://coveralls.io/repos/github/nonjene/webpack-papa-script/badge.svg?branch=master)](https://coveralls.io/github/nonjene/webpack-papa-script?branch=master) [![npm](https://img.shields.io/npm/v/webpack-papa-script.svg)](https://www.npmjs.com/package/webpack-papa-script) webpack-papa-script 是一个帮助前端开发更轻易地执行从开发到部署的工程化工具。 它是一个前端多页面、多个项目集成的解决方案, 方便部署大量短期性的页面, 例如大量的活动推广专题。方便集成共用组件, 快速部署, 免去频繁执行依赖包的安装。 ## 特点 - 基于 webpack v3, 贴紧webpack的生态 - 蜂巢式的大项目,可任意创建小项目 - 可单独或批量编译小项目,无需整个蜂巢项目进行编译 - 批量编译时,自动识别所有项目 - 自动识别页面入口 - 相互独立的小项目可共用组件代码 - 小项目可注入非 webpack 的公共代码包的引用, 公共包更新即对整个蜂巢项目起效 - 便捷的业务环境切换 - 灵活的项目部署配置 - 灵活的开发环境 - 快速部署 - 可通过模版创建小项目 - 集成了自动上传 ftp ## 如何使用 ### 创建一个项目 安装一个工具包 ```shell npm i create-webpack-papa -g ``` 创建一个命名为 "proj-hive" 的站点项目 ```shell create-webpack-papa create proj-hive ``` 等待依赖文件自动安装完成后, 进入项目, 即可使用 webpack-papa-script 的各种功能。 ```shell cd proj-hive ``` ### 开发一个小项目 创建一个命名为 "my-proj1" 单页初始小项目 ```shell npm run create my-proj1 ``` 对 my-proj1 进行本地开发 ```shell npm run watch my-proj1 ``` 部署上线代码 ```shell npm run build my-proj1 ``` 其他功能参考详细的[cli命令](#cli命令) ## 功能介绍 ### cli命令 通用地, 命令采用`npm run foo`模式, foo代表具体的命令名称, 后面可接其他参数。一些参数内容是与`papa.config.js`的配置对应的。 功能 | 例子 | 说明 --------------- | ----------------------------- | --- 创建新小项目 | `npm run create foo` | 创建一个foo小项目。支持层级的目录:假如是`src/2018/abc`, 则把`foo`换为`2018/abc` 选用特定的模版创建项目 | `npm run create foo t min` | 模版使用`src/_template_min` (默认模版是`_template_def`) 开启本地开发 | `npm run watch path/to/foo` | `path/to/foo` 代表`src/`下的文件夹 生成部署代码 | `npm run build path/to/foo``npm run build path/to/foo,path/to/bar` | 编译目录`path/to/foo`的代码; 可以指定多个位置, 以`,`间隔。默认是部署`config.productEnvType`指定的模式编译; 具体的编译效果请看[具体介绍](#build的介绍)。 批量生成部署代码 | `npm run build-all` | 自动查找`src/`下的所有的项目, 依次自动编译所有。 设置批量生成的范围 | `npm run build-all scope path/to/foo` | 自动查找`src/path/to/foo`下的所有的项目, 依次自动编译所有;同`build`命令, `scope`的值也可以包含多个, 以`,`间隔。 选择部署环境 | `npm run build foo test` | `pro``config.deployEnvType`中定义的key值, 默认是`test\|pre\|pro`. 上传ftp | `npm run build foo test u``npm start u foo` | `foo`在test模式编译后, 立即上传; 或指定上传的项目。(上传目录在`config.localAssetPath`设定。) 生成公共资源 | `npm run deploy-static` | 把`./resource/js`打包📦, 生成脱离webpack的公共代码包到`./resource/bundle`, 并把`./resource/bundle`的所有资源分发到`config.deployEnvType`配置的目录上(watch命令也会自动执行资源分发) 设置前端的环境变量 | `npm run watch foo mode pro` | 把本地开发的代码的环境切换为pro的环境。为了避免误操作, 只有在`config.developEnvType`设置的环境下才能使用`mode`(`watch`即为该环境)。假如确实需要在其他环境切换, 可以把`mode`换为`hard-mode` 强制编译环境为production | `npm run watch foo p` | 一般不需要使用, 某些情况为了调试或测试可用。此例子可把本地开发的编译效果改为像build那样 强制编译环境为development | `npm run build foo d` | 一般不需要使用, 某些情况为了调试或测试可用。效果与`p`相反。 ### build的介绍 webpack-papa-script 有非常灵活的编译功能, build的操作可以针对一个或多个或一个范围内的所有项目, 详见[使用build命令](#使用build命令)。 此外我们可以在项目下的`papa.config.js`自定义build的各项配置, 如: - [各种编译模式的定义](#编译模式的定义) - [编译代码的输出目录](#编译输出) - [定义如何识别项目](#定义如何识别项目)(约定优先, 不再需要手工配置入口, 只要目录按照这一个参考系数的规则创建, 即自动识别为一个项目) #### 使用build命令 先假设一个papa项目上有以下这样的目录结构, 它含有三个小项目`foo`,`bar/baz`,`bar/qux````shell |-build |-dist |-node_modules |-resource |-src |---foo |-----index.js |-----index.html |---bar |-----baz |-------index.js |-------index.html |-----qux |-------index.js |-------index.html ``` `npm run build` 后面接需要build的目标即可, 目标是一个基于项目目录`src`下的子目录, 如: - `npm run build foo` 代表编译`src/foo`这个项目, - `npm run build bar/baz` 代表`src/bar/baz`这个项目。 目标可以设置为多个, 一个命令build多个项目。用`,`隔开: - `npm run build foo,bar/baz` 同上面两条命令分别执行的效果。 目标可以设置为一个范围内的所有小项目, 如: - `npm run build-all` 代表编译`src`下的所有项目。 - `npm run build-all scope bar` 代表编译`src/bar`下的所有项目, 按照以上的例子, 则是`bar/baz``bar/qux`。 同样, 指定的范围也可以包含多个, 用`,`隔开。(注意例子中的`foo`本身是一个项目, 用`scope`是不能识别的) #### 编译模式的定义 编译模式分了两个概念, 一个是**前端环境**, 一个是**编译模式**。我们可以通过`papa.config.js`定义这些配置, 然后在命令行可以使用配置里所定义的名称。 ##### 前端环境 我们上线一个项目前通常需要经过几个过程, 如本地开发环境, 测试环境, 预发布环境等。我们通过`papa.config.js`定义这些前端环境, 并且将这些环境变量注入到前端代码里。相关的属性例子如下: `deployEnvType` key值定义有哪些环境, 值为编译的输出目录。如以下定义了 `test|pre|pro` 3个。 ```js deployEnvType:{ test: 'build/activity', pre: 'dist/pre', pro: 'dist/pro' } ``` `deployEnvMapFetch` 可以定义环境对应的前端环境变量, 该值会注入编译后的代码里。 ```js deployEnvMapFetch: { test: 'test', pre: 'pre', pro: 'produce' }, ``` `frontendConfCode` 就是注入的js代码模版, 它接收两个变量, 其中一个是`mode`即为前端环境变量。详细例子参考[使用方法](#如何使用)生成的`papa.config.js` 例子。 有了这些配置后, 我们就可以在命令中指定我们要使用哪一个环境: `npm run build foo [test|pre|pro]` 假如我们不指定环境的话, 它会自动选取 papa.config.js 的`productEnvType`定义的环境。 ##### 编译模式 固定了两种:开发模式和发布模式 我们可以用`developEnvType`定义上文中`deployEnvType`里的哪个前端环境为开发模式, 其它则自动作为发布模式: ```js developEnvType:{ deploy: 'test', fetch: 'test' } ``` `deploy` 是环境的命名, 需要是`deployEnvType`中的一个key值; `fetch` 是前端环境变量, 跑`watch`命令时会使用这个值, 一般上与`deployEnvMapFetch`对应的一致即可。 运行`watch`时, webpack的`devtool``#inline-source-map`。 运行`build`时, 假如前端环境等于`developEnvType`的定义, 则`devtool``eval`。其他情况则不声明 devtool。 当然可以通过`papa.config.js``webpackConfig`强制改写webpack配置属性。 #### 编译代码的输出目录 编译输出位置即上文的[前端环境](#前端环境)提到的`deployEnvType`中定义。 #### 定义如何识别项目 项目识别的参考值定义在 `papa.config.js`, 当目录中含有`projContainsOneOf`的值, 就识别为项目, 其中, 假如该目录拥有`proj.json`, 则识别为一个多页项目, 其它情况就是单页项目。 具体介绍如下: | 参数名 | 默认值 | 描述 | | --------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | projContainsOneOf | `['m', 'pc', 'proj.json', 'config.json']` | 辨别一个项目时, 只要一个文件夹里面包含此属性定义的文件或文件夹名, 则认定它为一个项目。(无论单独页面还是多页面) | | projScanExclude | `['modules','components', 'img', 'js']` | 获取所有项目时, 排除以下这些文件夹里面的内容 | | entryInclude | `['index.js', 'index.html']` | 入口文件夹必须包含这些文件。一般不要更改这个值, 其中包含 index.js, webpack才能正常编译 | | commSingleProjSubPage | `['m', 'pc']` | 可以定义通用的, 每个小项目可以默认包含这些子页面, 它们共享`config.json`, 无需每个这种页面都添加 多页项目识别文件`proj.json`。 比如一个单页项目, 不适合做响应式, 需要包含电脑端和移动端两个页面。可以定义为空, 则忽略掉这个情况。具体请看默认的初始化项目中的模版项目。 | ### 部署非webpack的公共静态资源 公共静态资源存放在`resource/bundle`文件夹中, js资源放在`resource/js`。 运行`npm run deploy-static`部署后, 会按照`config.staticFileConcatOrder`, 在`js`文件夹内查找到, 合并压缩至`resource/bundle`, 并把所有bundle资源分发到`config.deployEnvType`定义的所有文件夹。 运行`npm run watch` 时, 会自动把bundle复制到`config.developEnvType`对应的文件夹, 无需手动运行`npm run deploy-static`。 合并的 js 资源将会在所有编译后的页面的html中插入引用。 ## 项目风格 ### 项目部署配置 通过`papa.config.js`配置文件, 可自定义各项部署功能, 比如可定义部署的环境, 输出路径, 让生产代码是否兼容ie8, 或改写webpack配置。详见[项目配置](#项目配置) ### 自动识别小项目及页面入口 避开了 webpack 的单项目的特性, 只需按照简单的约定规则新建一个页面目录, 即自动识别为一个 webpack 编译的入口。 如上文[定义如何识别项目](#定义如何识别项目)所介绍, 一个目录含有`proj.json`就识别为一个多页项目, 否则假如符合`projContainsOneOf`的范围的话, 则识别为一个单页项目。 `proj.json`文件只作为标记,内容可为空。 多页项目里, 程序将寻找所有符合`entryInclude`的识别为页面;单页项目假如含有`commSingleProjSubPage`定义的子页面的话, 则自动识别为拥有子页面, 参考[html风格](#html风格) ### html风格 每个页面的html文件只需包含业务代码, 编译后会将内容传入html模版。 每个页面都包含一个`config.json`文件, 用于定义html模版的变量, 如: ```shell |-foo |---config.json |---index.js |---index.html ``` 假如定义了`commSingleProjSubPage`, 而且项目中含有这些子页面, 则json文件放在子页面文件夹的同级, 假如该值是 `['m','pc']`, 则如下: ```shell |-foo |---config.json |---m |-----index.js |-----index.html |---pc |-----index.js |-----index.html ``` 默认的, 用`npm run create`命令创建的模版项目是以上第二个的目录结构。 `config.json`有如下示例: 属性 | 默认值 | 描述 --|--|-- title| `标题` | html的`<title/>` htmlFile | `index.html` | 指定html文件的名字 templateName_xx | `index_xx.handlebars` | 指定html模版文件。xx 是假如此页面包含 `commSingleProjSubPage`定义的子页面, xx就是该子页面名字。 templateName_comm | `index_comm.handlebars` | 假如该页面不包含`commSingleProjSubPage`的子页面, 则使用这个来指定模版 bodyProp | `""` | `<body/>`的属性 比如`"class=\"top\" data-version=\"3.0.0\""` moreMeta | `""` | `<head/>`内添加内容。如`"<link rel=\"dns-prefetch\" href=\"https://www.github.com\">"` 模版文件位于`resource/html`。 如果项目目录不包含 config.json, 将不会使用`html模版`, 而会把目录顶层的`index.html`视为完整的 html 文件(webpack 编译出来的资源会自动填入) ### 组件 每个小项目互相独立, 但可以共用模块。两种实现方式: 1. **共用组件** 所有小项目都可以 require 共用组件。因为是独立编译, 项目之间不会互相影响, 组件更新后, 各个项目需重新编译才会生效。这可避免在更新组件后, 因项目数量庞大, 意外地应用在不应生效的其他项目而导致意外的错误, 从而降低更新代码的负担。 1. **公共代码包** 所有小项目都会引用共代码包, 此包有修改, 所有已在线上的项目都能获取到更新的公共包, 无需重新编译。详见[部署非webpack的公共静态资源](#部署非webpack的公共静态资源) ### 业务环境切换 可在编译时在项目的业务代码里设置、切换不同的内容, 让开发过程更为灵活。比如可应用于部署环境的切换及fetch数据的环境切换; ### 本地开发环境 本地开发采用 webpack-dev-server, 默认开启了热更新功能, 并且整合了更灵活的代理功能。 代理功能可将本地开发打通其他项目环境, 比如可共用cookie等缓存。 可以正则匹配路径。 在一些场景中, 比如我们这个项目并非一个完全独立的站点, 它还需要与其他项目联动, 那么代理功能就非常实用了。我们可以通过`papa.config.js``proxy` 设置代理, 可以通过正则匹配路径实现代理, 并可以设置多个匹配规则。 如以下配置, 代理了路径以非 "activity" 开头的所有请求 到本地的80端口: ```js proxy:[ { filterPathname: /^\/(?!activity\/)/, target: 'http://localhost:80', }, ], ``` ## 集成功能 webpack-papa-script 基于webpack, 已集成所有常见资源的处理, 以下是集成功能的列表: * **ES6 -> ES5** * **React** * 集成**babel-runtime**, Promise随便用 * 可开启ie8兼容, 把代码转为ES3 在`resource/html/index_pc.handlebars`模版里, 加了ie8以下 es5-slim和 sham 文件的引用, 我们可以按照实际场景把该引用链接更换。 该文件在`resource/bundle/`中已预置, 执行 ```shell npm run deploy-static ``` 即可把es5-slim-sham文件复制到部署目录中。 * **handlebar** * **sass-postcss-autoprofixer** * **webpack-dev-server** 本地开发集成热更新及代理 * **extract-text-webpack-plugin** build时独立出css文件 ### 编译支持的文件格式 * js[x] * [s]css * png|jpg|gif|svg * handlebars|tmpl * woff|woff2|eot|ttf * pdf ### 编译支持的特殊文件 * **sw.js** 使用 file-loader 编译, require后就会把该文件独立释出到输出目录;输出文件名带hash值, 可确保sw更新被触发。 * **manifest.json** 同样使用 file-loader 编译, 但不带hash值, 便于固定写在html模版。 * **\*.iso.(png|gif)** 绝对不合并到css文件。其他的小于 4KB 的图片将转为 base64 合并入css文件 以下内容因有不理想的效果, 所以没有支持: * **图片压缩功能** 转换png时, 现有的图片压缩插件是把png24转为png8, 会导致图片低质量, 所以在找到更好的压缩功能后再添加该功能。 * **`.html`文件内的资源引用** 如index.html假如有`<img src="foo.jpg"/>`, foo.jpg无法解析。这类功能请转移到css、React 或 handlebars实现。 ### eject 假如 webpack-papa-script 满足不了项目的需要, 随时可运行 `create-webpack-papa eject`, 把 webpack-papa-script 的代码转移到项目目录上, 对构建代码自行修改。不过需要注意的是, 该操作无法逆转, 操作前请确认清楚。 ## 项目配置 以下是默认配置, 我们可以在项目的 papa.config.js 修改这些属性: ```js { // 自动上传ftp需要的配置信息 ftp: { host: '192.168.1.1', port: '', user: 'user', password: 'ps' }, // ftp的根目录 remoteBasePath: '', // ftp的目录 remotePath: '/activity/{$target}/', // 需要上传到ftp的根目录 localAssetPath: 'build/activity', // 部署上线时的host domainName: 'http://m.okpapa.com', // js,css,image 等资源的host cdnDomain: 'https://images.okpapa.com', // 代理信息, 数据类型: object array proxy:[ { filterPathname: /^\/(?!activity\/)/, // 代理 pathname 以非 activity开头的所有请求 target: 'http://localhost:80', }, ], //本地开发环境的服务端口 servePort: 3005, // 本地开发环境的静态资源基目录(同 devServer.contentBase) serveContentBase:'./build/', // `resource/js` 中的js文件的合并顺序, 合并生成脱离webpack的公共代码包 staticFileConcatOrder: [], // 合并js的命名 staticFileName:'common.js', // 脱离webpack的公共文件的输出子路径 staticFileSubPath:'static', // 是否启用自带的plugin defPlugin:{ sri: true, //webpack-subresource-integrity, 非https的话,请设为false uglifyJs: true //是否压缩js }, // 加入其他 webpack 插件 customPlugin:{ production: [], // 正式环境 development: [] // 开发环境 }, /** * 即将执行编译前的时候,根据这个函数返回决定是继续还是中断。可为空,空则忽略。分正式环境和开发环境 * aTargets {array} 用户输入所有项目的完整路径 * return {boolean} false 则中断编译 */ shouldCompileProceed: { production: (aTargets)=>{return true}, development: (aTargets)=>{return true}, }, // 覆盖预置的webpack配置 webpackConfig: {}, // 是否支持ie8 kiss_ie8: true, // 定义一个页面下面还分哪些版本页面。 // 比如一个单页项目, 不适合做响应式, 需要包含电脑端和移动端两个页面。可以定义为空, 则忽略掉这个情况 commSingleProjSubPage:['m', 'pc'], // 辨别一个项目时, 只要一个文件夹里面包含以下文件或文件夹, 则认定它为一个项目。(无论单独页面还是多页面) projContainsOneOf: ['m', 'pc', 'proj.json', 'config.json'], // 获取所有项目时, 排除以下这些文件夹里面的内容(不会在已识别为proj的文件夹里再查找) projScanExclude:['modules', 'module', 'static', 'components', 'component', 'img', 'js'], // 验证 webpack 入口必须包含这个值的所有文件。 entryInclude: ['index.js', 'index.html'], //本地开发环境的命名和前端代码注入的环境变量名。 developEnvType: { deploy: 'test', //命名 fetch: 'test' //环境变量名 }, //正式上线的环境 productEnvType: { deploy: 'pro', fetch: 'produce' }, //环境名称对应的输出路径 deployEnvType: { pre: 'dist/pre', pro: 'dist/pro', test: 'build/activity' }, //环境名称对应的前端环境变量名 deployEnvMapFetch: { pre: 'pre', pro: 'produce', test: 'test' }, // 环境名称的中文名, 用于命令行的显示 releaseEnvDesc: { pre: '预发环境😛', pro: '生产环境😝', test: '开发环境🤔' }, // 前端环境的中文名 fetchEnvDesc: { pre: '预发环境🥑', test: '测试环境🥝', produce: '生产环境🍓' }, // 前端代码注入内容 // mode: 前端环境变量 // debug: 环境与productEnvType一致时, 则为false, 否则true frontendConfCode:`try{ Object.assign(window.publicConfig, { mode:"{$mode}", debug:{$debug} }); }catch(e){}`, }, ```