前端总结1
一、css
1、Flex 布局
参考:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
1)
flex-direction
属性决定主轴的方向(即项目的排列方向)
它可能有4个值。
row
(默认值):主轴为水平方向,起点在左端。row-reverse
:主轴为水平方向,起点在右端。column
:主轴为垂直方向,起点在上沿。column-reverse
:主轴为垂直方向,起点在下沿。
.top-banner { text-align: center; display: flex; flex-direction: column; justify-content: center; }
2)flex-grow
属性定义项目的放大比例,默认为0
,即如果存在剩余空间,也不放大。
如果所有项目的flex-grow
属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow
属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
3)flex-shrink
属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的flex-shrink
属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink
属性为0,其他项目都为1,则空间不足时,前者不缩小。
4)flex-basis
属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto
,即项目的本来大小。
5)flex
规定了弹性元素如何伸长或缩短以适应flex容器中的可用空间。这是一个简写属性,用来设置 flex-grow
, flex-shrink
与 flex-basis,默认值为
。0 1 auto
默认情况下,元素不会缩短至小于内容框尺寸,若想改变这一状况,请设置元素的min-width
与 min-height
属性。
flex
属性可以指定1个,2个或3个值。
单值语法: 值必须为以下其中之一:
- 一个无单位数(
<number>
): 它会被当作<flex-grow>的值。
- 一个有效的宽度(
width
)值: 它会被当作<flex-basis>的值。
- 关键字
none
,auto
或initial
.
双值语法: 第一个值必须为一个无单位数,并且它会被当作 <flex-grow>
的值。第二个值必须为以下之一:
- 一个无单位数:它会被当作
<flex-shrink>
的值。 - 一个有效的宽度值: 它会被当作
<flex-basis>
的值。
三值语法:
- 第一个值必须为一个无单位数,并且它会被当作
<flex-grow>
的值。 - 第二个值必须为一个无单位数,并且它会被当作
<flex-shrink>
的值。 - 第三个值必须为一个有效的宽度值, 并且它会被当作
<flex-basis>
的值。
二、js
1、Promise 对象
参考:http://es6.ruanyifeng.com/#docs/promise
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) }); // 出错了
Promise 实例具有then
方法,也就是说,then
方法是定义在原型对象Promise.prototype
上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then
方法的第一个参数是resolved
状态的回调函数,第二个参数(可选)是rejected
状态的回调函数。then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。Promise.prototype.catch
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
上面代码中,getJSON
方法返回一个 Promise 对象,如果该对象状态变为resolved
,则会调用then
方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected
,就会调用catch
方法指定的回调函数,处理这个错误。另外,then
方法指定的回调函数,如果运行中抛出错误,也会被catch
方法捕获。一般来说,不要在then
方法里面定义 Reject 状态的回调函数(即then
的第二个参数),总是使用catch
方法。
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then
方法执行中的错误,也更接近同步的写法(try/catch
)。因此,建议总是使用catch
方法,而不使用then
方法的第二个参数。一般总是建议,Promise 对象后面要跟catch
方法,这样可以处理 Promise 内部发生的错误。catch
方法返回的还是一个 Promise 对象,因此后面还可以接着调用then
方法。
三、webpack
1、代码分离
参考:https://webpack.docschina.org/guides/code-splitting/
https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
1)入口起点
这是迄今为止最简单、最直观的分离代码的方式。不过,这种方式手动配置较多,并有一些隐患。
// webpack.config.js module.exports = { entry: { index: './src/index.js', another: './src/another-module.js' } };
隐患:如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中;这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。
分离 app(应用程序) 和 vendor(第三方库) 入口:在 webpack < 4 的版本中,通常将 vendor 作为单独的入口起点添加到 entry 选项中,以将其编译为单独的文件(与 CommonsChunkPlugin
结合使用)。而在 webpack 4 中不鼓励这样做。而是使用 optimization.splitChunks
选项,将 vendor 和 app(应用程序) 模块分开,并为其创建一个单独的文件。不要 为 vendor 或其他不是执行起点创建 entry。
2)防止重复
SplitChunksPlugin
插件可以将公共的依赖模块提取到已有的 entry chunk 中,或者提取到一个新生成的 chunk。
3)动态导入
当涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 import()
语法 来实现动态导入。
动态地加载模块。调用 import()
之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。
// 在注释中我们提供了 webpackChunkName。这样会将拆分出来的 bundle 命名为如mainPage.xxxx.js export default new Router({ mode: process.env.VUE_APP_ROUTE_MODE, base: process.env.BASE_URL, routes: [ { path: '/', redirect: '/main' }, { path: '/main', component: AppMain, redirect: '/main/index', children: [{ path: 'index', name: 'index', component: () => import(/* webpackChunkName: "mainPage" */ './views/Index'), // 首页 meta: { title: '首页' } }, { path: 'prdRank', name: 'prdRank', component: () => import(/* webpackChunkName: "mainPage" */ './views/PrdRank'), // 游戏库 meta: { title: '游戏库' } }, { path: 'orderList', name: 'orderList', component: () => import(/* webpackChunkName: "mainPage" */ './views/OrderList'), // 我的订单 meta: { title: '我的订单' } }] }, { path: '/prdList', name: 'prdList', component: () => import(/* webpackChunkName: "prdPage" */ './views/PrdList') // 商品列表 }, { path: '/prdDetail', name: 'prdDetail', component: () => import(/* webpackChunkName: "prdPage" */ './views/PrdDetail'), // 商品详情 meta: { title: '商品详情' } } ] })
webpackChunkName
:新 chunk 的名称。
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
把组件按组分块:有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
4)预取/预加载模块
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 "resource hint(资源提示)",来告知浏览器:
- prefetch(预取):将来某些导航下可能需要的资源
- preload(预加载):当前导航下可能需要资源
与 prefetch 指令相比,preload 指令有许多不同之处:
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
- 浏览器支持程度不同。
2、缓存
我们使用 webpack 来打包模块化的应用程序,webpack 会生成一个可部署的 /dist
目录,然后把打包后的内容放置在此目录中。只要 /dist
目录中的内容部署到 server 上,client(通常是浏览器)就能够访问网站此 server 的网站及其资源。而最后一步获取资源是比较耗费时间的,这就是为什么浏览器使用一种名为 缓存 的技术。可以通过命中缓存,以降低网络流量,使网站加载速度更快,然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。通过必要的配置,以确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。webpack 提供了一种使用称为 substitution(可替换模板字符串) 的方式,通过带括号字符串来模板化文件名。其中,[contenthash]
substitution 将根据资源内容创建出唯一 hash。当资源内容发生变化时,[contenthash]
也会发生变化。
将第三方库(library)(例如 lodash
或 react
)提取到单独的 vendor
chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用 client 的长效缓存机制,命中缓存来消除请求,并减少向 server 获取资源,同时还能保证 client 代码和 server 代码版本一致。
// webpack.config.js optimization: { splitChunks: { cacheGroups: { vendors: { name: 'chunk-vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, chunks: 'initial' } }
} }
而 vendor
hash 发生变化是我们要修复的。幸运的是,可以使用两个插件来解决这个问题。第一个插件是 NamedModulesPlugin
,将使用模块的路径,而不是一个数字 identifier。虽然此插件有助于在开发环境下产生更加可读的输出结果,然而其执行时间会有些长。第二个选择是使用 HashedModuleIdsPlugin
,推荐用于生产环境构建。