|
| 1 | +=== |
| 2 | + |
| 3 | +标题: [web前端] JS: reduce 方法实现 webpack 多文件入口 |
| 4 | +标签: 前端工程化, nodejs |
| 5 | + |
| 6 | +=== |
| 7 | + |
| 8 | +> 这篇日志,在开始接触 webpack 时候就该写了,现在发布也许对一些刚入此坑的童鞋能些许帮助。。。 |
| 9 | +即使有点 low,重要的仍是分享 |
| 10 | + |
| 11 | +## 1. reduce 方法介绍 |
| 12 | + |
| 13 | +### 1.1 简单场景 |
| 14 | + |
| 15 | +reduce 函数的设计意图就是方便进行叠加运算: |
| 16 | + |
| 17 | +```js |
| 18 | +var arr = [0, 1, 2, 3]; |
| 19 | + |
| 20 | +// reduce 实现累加 |
| 21 | +var total = arr.reduce(function (pre, cur){ |
| 22 | + return pre + cur; |
| 23 | +}, 0); |
| 24 | + |
| 25 | +console.log(total); // 6 |
| 26 | +``` |
| 27 | + |
| 28 | +上述代码中,reduce 方法有两个参数,第一个参数是一个 callback,用于进行计算的函数;第二个参数则是累加计算的初始值: 0 |
| 29 | +reduce 以 0 作为初始值,从数组第 0 项开始累加,上述代码的计算过程如下: |
| 30 | + |
| 31 | +```js |
| 32 | +total = 0; // => 0 |
| 33 | +total = 0 + 0; // => 0 |
| 34 | +total = 0 + 1; // => 1 |
| 35 | +total = 1 + 2; // => 3 |
| 36 | +total = 3 + 3; // => 6 |
| 37 | +``` |
| 38 | + |
| 39 | +若不设置初始值 0,则 reduce 以数组第 0 项作为初始值,从第 1 项开始累加,其计算过程如下: |
| 40 | + |
| 41 | +```js |
| 42 | +total = 0; // => 0 |
| 43 | +total = 0 + 1; // => 1 |
| 44 | +total = 1 + 2; // => 3 |
| 45 | +total = 3 + 3; // => 6 |
| 46 | +``` |
| 47 | + |
| 48 | +可以看出,reduce 函数根据初始值 0,不断进行叠加,完成最简单的数组累加。 |
| 49 | + |
| 50 | +### 1.2 两种简单的运用场景 |
| 51 | + |
| 52 | +第一个 demo,使用 reduce 函数进行二维数组的拼接: |
| 53 | + |
| 54 | +```js |
| 55 | +var arr = [ [0], [1, 2], [3, 4, 5] ]; |
| 56 | + |
| 57 | +// reduce 实现数组拼接 |
| 58 | +var result = arr.reduce(function (pre, cur){ |
| 59 | + return pre.concat(cur); |
| 60 | +}, []); |
| 61 | + |
| 62 | +console.log(result); // [0, 1, 2, 3, 4, 5] |
| 63 | +``` |
| 64 | + |
| 65 | +第二个 demo,使用 reduce 函数构造 JSON 数组: |
| 66 | + |
| 67 | +```js |
| 68 | +// 此例演示:将所有员工的姓名进行拆分 |
| 69 | +var staff = ['Bob Dell', 'Johon Jobs', 'Maria July']; |
| 70 | + |
| 71 | +// reduce 构造 JSON 数组 |
| 72 | +var result = staff.reduce(function (arr, full_name){ |
| 73 | + arr.push({ |
| 74 | + first_name: full_name.split(' ')[0], |
| 75 | + last_name: full_name.split(' ')[1] |
| 76 | + }); |
| 77 | + |
| 78 | + return arr; |
| 79 | +}, []); |
| 80 | + |
| 81 | +console.log(JSON.stringify(result)); |
| 82 | +// [{"first_name":"Bob","last_name":"Dell"},{"first_name":"Johon","last_name":"Jobs"},{"first_name":"Maria","last_name":"July"}] |
| 83 | +``` |
| 84 | + |
| 85 | +灵活使用 reduce 函数,能为我们节省不少中间变量和代码。 |
| 86 | + |
| 87 | +## 2. 用于实现 webpack 多文件入口配置 |
| 88 | + |
| 89 | +webpack 配置项中```entry```参数用于配置入口文件路径,通常对于只打包一个目录下的文件,只需要遍历该目录,构造一个如下的对象传递给```entry```即可: |
| 90 | + |
| 91 | +```js |
| 92 | +// 注:index.js 为每个页面的入口文件,所有页面均在 ./fe/pages/ 目录下 |
| 93 | +var entry = { |
| 94 | + index: './fe/pages/home/index.js', |
| 95 | + list: './fe/pages/list/index.js' |
| 96 | +}; |
| 97 | +``` |
| 98 | + |
| 99 | +通常,我们使用 reduce 方法来遍历同一目录下的入口: |
| 100 | + |
| 101 | +```js |
| 102 | +var fs = require('fs'); |
| 103 | +var path = require('path'); |
| 104 | +... |
| 105 | + |
| 106 | +// 定义入口路径 |
| 107 | +var entryPath = './fe/pages'; |
| 108 | + |
| 109 | +// 遍历路径下多文件入口 |
| 110 | +var entris = fs.readdirSync(entryPath).reduce(function (o, filename) { |
| 111 | + !/\./.test(filename) && |
| 112 | + (o[filename] = './' + path.join(entryPath, filename, 'index.js')); |
| 113 | + return o; |
| 114 | +}, {}); |
| 115 | + |
| 116 | +// entry = { |
| 117 | +// index: './fe/pages/home/index.js', |
| 118 | +// list: './fe/pages/list/index.js' |
| 119 | +// } |
| 120 | +``` |
| 121 | + |
| 122 | +对于多页面应用的开发场景,也许会需要构造类似于下面这样的一个对象: |
| 123 | + |
| 124 | +```js |
| 125 | +// 多个入口,页面、公共组件并不一定在同一个目录下 |
| 126 | +var entry = { |
| 127 | + index: './fe/pages/home/index.js', |
| 128 | + list: './fe/pages/list/index.js', |
| 129 | + header: './fe/components/header/index.js', |
| 130 | + footer: './fe/components/footer/index.js' |
| 131 | +}; |
| 132 | +``` |
| 133 | + |
| 134 | +可以发现,我们要打包的页面、公共组件不一定在同一个目录下,这时候就需要对原先的方法进行扩展,见代码: |
| 135 | + |
| 136 | +```js |
| 137 | +var fs = require('fs'); |
| 138 | +var path = require('path'); |
| 139 | +... |
| 140 | + |
| 141 | +// 定义入口路径 |
| 142 | +var entryPath = ['./fe/pages', './fe/components']; |
| 143 | + |
| 144 | +// 遍历路径下多文件入口 |
| 145 | +var mkEntriesMap = function (entryPath){ |
| 146 | + if (typeof(entryPath) == 'string') { // 若 entryPath 为字符串,则直接遍历此目录 |
| 147 | + var path_map = fs.readdirSync(entryPath).map(function (filename){ |
| 148 | + return filename + '::./' + path.join(entryPath, filename, 'index.js'); |
| 149 | + }); |
| 150 | + } else if (typeof(entryPath) == 'object') { // 若 entryPath 为数组,则进行两级遍历 |
| 151 | + var path_map = entryPath.map(function (entry){ |
| 152 | + return fs.readdirSync(entry).map(function (filename){ |
| 153 | + return filename + '::./' + path.join(entry, filename, 'index.js'); |
| 154 | + }); |
| 155 | + }).reduce(function (preArr, curArr){ |
| 156 | + return preArr.concat(curArr); |
| 157 | + }, []); |
| 158 | + } else { |
| 159 | + throw 'Type of config.entryPath is not valid.'; |
| 160 | + return; |
| 161 | + } |
| 162 | + |
| 163 | + return path_map.reduce(function (o, file_map){ |
| 164 | + var file_name = file_map.split('::')[0]; |
| 165 | + var file_path = file_map.split('::')[1]; |
| 166 | + |
| 167 | + if (!/\./.test(file_name)) { |
| 168 | + o[file_name] = file_path; |
| 169 | + } |
| 170 | + |
| 171 | + return o; |
| 172 | + }, {}); |
| 173 | +}; |
| 174 | + |
| 175 | +// 构造对象 |
| 176 | +var entris = mkEntriesMap(entryPath); |
| 177 | + |
| 178 | +// entry = { |
| 179 | +// index: './fe/pages/home/index.js', |
| 180 | +// list: './fe/pages/list/index.js', |
| 181 | +// header: './fe/components/header/index.js', |
| 182 | +// footer: './fe/components/footer/index.js' |
| 183 | +// } |
| 184 | +``` |
| 185 | + |
| 186 | +这样做的好处在于,只需配置一开始的```entryPath```就行了,同时支持单个或多个路径下的文件打包: |
| 187 | + |
| 188 | +```js |
| 189 | +// entryPath 可以为一个字符串 |
| 190 | +var entryPath = './fe/pages'; |
| 191 | + |
| 192 | +// entryPath 也可以设为一个数组 |
| 193 | +var entryPath = ['./fe/pages', './fe/components']; |
| 194 | +``` |
0 commit comments