packagejson-中-你还不清楚的-browsermodulemain-字段优先级

browser VS module VS main前端开发中使用到 npm 包那可算是家常便饭,而使用到 npm 包总免不了接触到 package.json 包配置文件。 那么这里就有一个问题,当我们在不同环境下 import 一个 npm 包时,到底加载的是 npm 包的哪个文件? 老司机们很快地给出答案:main 字段中指定的文件。 然而我们清楚 npm 包其实又分为: 只允许在客户端使用的,只允许造服务端使用的,浏览器/服务端都可以使用。如果我们需要开发一个 npm 包同时兼容支持 web端 和 server 端,需要在不同环境下加载npm包不同的入口文件,显然一个 main 字段已经不能够满足我们的需求,这就衍生出来了 module 与 browser 字段。 本文就来说下 这几个字段的使用场景,以及同时存在这几个字段时,他们之间的优先级。 文件优先级在说 package.json 之前,先说下文件优先级 由于我们使用的模块规范有 ESM 和 commonJS 两种,为了能在 node 环境下原生执行 ESM 规范的脚本文件,.mjs 文件就应运而生。 当存在 index.mjs 和 index.js 这种同名不同后缀的文件时,import './index' 或者 require('./index') 是会优先加载 index.mjs 文件的。 也就是说,优先级 mjs > js ...

June 11, 2019 · 2 min · jiezi

前端培训初级阶段场景实战20190613Nginx代理正确食用方式

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。 截止到 2019-05-30 期,所有成员都进行了一次分享。内部对课程进行了一些调整,之后会针对项目开始 review 。我这边预期准备进入中级阶段,中间还是会穿插一些实战。 前端培训目录 今天讲什么?nginx 的 servernginx 的 location 匹配规则nginx 的 root、rewrite、proxy_pass、aliasnginx 的命令以及报错日志今天为什么会开这个题目? 公司内部的前端构建工具升级(gulp),帮小伙伴处理了一下 nginx 的配置,辅助提升开发的体验。公司想要加快网页访问速度(前端缓存),为了测试,我改了我自己服务器的 nginx 配置。 PWA ()manifest ()其他方案(localStroage存)有老哥有科学有效的方案吗?缓存这块我还在实验中,我司有结果之后我会写个文章发出来。nginx 的 server定义虚拟主机相关。server 中通过 server_name 来匹配域名,listen来匹配端口 server_name用于匹配域名,需要已经映射的域名。举个栗子,我在阿里云有一台云服务器 IP:123.56.16.33:443。买了一个域名 lilnong.top。我现在把我的域名指向了我的ip。那所有请求我域名的都会到我这台服务器上。我需要用 server_name 来判断请求的是那台主机,再进行分发 listen用于匹配端口号,一般来说,我们当做服务的就需要加上 80 和 443 协议端口用途http80浏览器访问https443浏览器访问ftp21 server_name 与 host 匹配优先级完全匹配通配符在前的,如 *.lilnong.top在后的,如 www.lilnong.*正则匹配,如 ~^\.www\.lilnong\.com$如果都不匹配 优先选择 listen 配置项后有 default 或 default_server 的找到匹配 listen 端口的第一个 server 块nginx 的 location 匹配规则location 是什么?location 是用于在 server 服务中,根据 URL 进行匹配查找。属于 ngx_http_core_module 模块。 ...

June 10, 2019 · 2 min · jiezi

面试必备webpack-中那些最易混淆的-5-个知识点

面试必备!webpack 中那些易混淆的 5 个知识点前两天为了优化公司的代码打包项目,恶补了很多 webpack4 的知识。要是放在几年前让我学习 webpack 我肯定是拒绝的,之前看过 webpack 的旧文档,比我们内部项目的文档还要简陋。 但是最近看了一下 webpack4 的文档,发现 webpack官网的 指南 写的还不错,跟着这份指南学会 webpack4 基础配置完全不是问题,想系统学习 webpack 的朋友可以看一下。 今天我主要分享的是一些 webpack 中的易混淆知识点,也是面试的常见内容。我把这些分散在文档和教程里的内容总结起来,目前看是全网独一份,大家可以加个收藏,方便以后检索和学习。 <br/> 友情提示:本文章不是入门教程,不会费大量笔墨去描写 webpack 的基础配置,请读者配合教程源代码食用。<br/> 1.webpack 中,module,chunk 和 bundle 的区别是什么?说实话我刚开始看 webpack 文档的时候,对这 3 个名词云里雾里的,感觉他们都在说打包文件,但是一会儿 chunk 一会儿 bundle 的,逐渐就迷失在细节里了,所以我们要跳出来,从宏观的角度来看这几个名词。 webpack 官网对 chunk 和 bundle 做出了解释,说实话太抽象了,我这里举个例子,给大家形象化的解释一下。 首先我们在 src 目录下写我们的业务代码,引入 index.js、utils.js、common.js 和 index.css 这 4 个文件,目录结构如下: src/├── index.css├── index.html # 这个是 HTML 模板代码├── index.js├── common.js└── utils.jsindex.css 写一点儿简单的样式: ...

May 29, 2019 · 5 min · jiezi

2019-前端框架对比及评测

Jacek Schae 原作,授权 LeanCloud 翻译。 我们将基于 RealWorld 示例应用对比前端框架。RealWorld 示例应用的特点: RealWorld 应用比待办事项类应用更复杂。通常待办事项类应用不足以传达足够多的知识见解构建实际应用。 标准化项目遵循特定规则。提供后端 API、静态标记语言、风格、API 规范。 专业人士编写、审阅理想情况下,会是高一致性、高真实度的项目,由使用该技术的专业人士编写或审阅。 比较的库和框架撰写本文时,RealWorld 示例应用仓库共包括 18 个 Conduit (Medium.com 克隆应用)实现。 本文不考虑框架的流行程度,RealWorld 仓库中列出的前端框架皆纳入对比范围。 测度性能应用显示内容、可以使用需要花多久? 尺寸应用有多大?我们只比较编译后的 JavaScript 文件大小。所有应用使用同样的 CSS 样式文件,CSS 文件加载自 CDN。所有应用使用的 HTML 也是一样的。这些框架都支持编译或转换为 JavaScript,所以我们仅仅测量 JavaScript 文件大小。 代码行数根据规范创建 RealWorld 应用需要多少行代码?公平地说,某些实现的功能要略微多一点,但这应该没有什么显著的影响。我们仅仅测量每个应用的 src/ 目录。 性能我们将使用 Chrome 的 [Lighthouse Audit] 测试性能。Lighthouse 返回 0 至 100 间的评分。0 为最低分。 配置所有测试均使用如下配置: 性能评分基于以下测度得出: First Contentful Paint (页面中内容元素首次渲染时间)First Meaningful Paint (页面中有意义的内容元素首次渲染时间)Speed Index (页面加载过程视觉上的变化速度)First CPU Idle (到 CPU 首次空闲的时间)Time to Interactive (到页面可交互的时间)Estimated Input Latency (预计输入延迟)详见 Lighthouse 评分指南。 ...

April 24, 2019 · 1 min · jiezi

前端面试题 -- webpack

前言上一篇 前端面试题-小程序随着前端的不断发展,现代前端开发的复杂度和规模越来越庞大。工程化的思想催生了很多流行框架的进程,作为现在最流行的前端构建工具–webpack,很多面试、工作场景中都会出现了它的身影。所以对于现在的前端来说,了解并能够使用webpack,无论对个人技能或者职场求职来说,都是一种有力的提升感兴趣的小伙伴可以点击 这里,查看完整版前端面试题如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓1. 对webpack的了解官方文档本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),将项目当作一个整体,通过一个给定的的主文件,webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包成一个或多个浏览器可识别的js文件核心概念:入口(entry)入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)module.exports = { entry: ‘./path/to/my/entry/file.js’};输出(output)output 属性告诉 webpack 在哪里输出它所创建的 bundles ,以及如何命名这些文件,默认值为 ./distloaderloader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)插件(plugins)loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量模式通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化module.exports = { mode: ‘production’};2. webpack,里面的webpack.config.js怎么配置let webpack = require(‘webpack’);module.exports = { entry:’./entry.js’, //入口文件 output:{ //node.js中__dirname变量获取当前模块文件所在目录的完整绝对路径 path:__dirname, //输出位置 filename:‘build.js’ //输入文件 }, module:{ // 关于模块的加载相关,我们就定义在module.loaders中 // 这里通过正则表达式去匹配不同后缀的文件名,然后给它们定义不同的加载器。 // 比如说给less文件定义串联的三个加载器(!用来定义级联关系): rules:[ { test:/.css$/, //支持正则 loader:‘style-loader!css-loader’ } ] }, //配置服务 devServer:{ hot:true, //启用热模块替换 inline:true //此模式支持热模块替换:热模块替换的好处是只替换更新的部分,而不是页面重载. }, //其他解决方案配置 resolve:{ extensions:[’’,’.js’,’.json’,’.css’,’.scss’] }, //插件 plugins:[ new webpack.BannerPlugin(‘This file is create by baibai’) ]}3. webpack本地开发怎么解决跨域的下载 webpack-dev-server 插件配置 webpack.config.js 文件// webpack.config.jsvar WebpackDevServer = require(“webpack-dev-server”);module.exports = { … devServer: { … port: ‘8088’, //设置端口号 // 代理设置 proxy: { ‘/api’: { target: ‘http://localhost:80/index.php’, // 目标代理 pathRewrite: {’^/api’ : ‘’}, // 重写路径 secure: false, // 是否接受运行在 HTTPS 上 } } }}4. 如何配置多入口文件配置多个入口文件entry: { home: resolve(__dirname, “src/home/index.js”), about: resolve(__dirname, “src/about/index.js”)}5. webpack与grunt、gulp的不同三者都是前端构建工具grunt 和 gulp 是基于任务和流的。找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程webpack 是基于入口的。webpack 会自动地递归解析入口所需要加载的所有资源文件,然后用不同的 Loader 来处理不同的文件,用 Plugin 来扩展 webpack 功能webpack 与前者最大的不同就是支持代码分割,模块化(AMD,CommonJ,ES2015),全局分析为什么选择webpack6. 有哪些常见的Loader?他们是解决什么问题的css-loader:加载 CSS,支持模块化、压缩、文件导入等特性style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSSslint-loader:通过 SLint 检查 JavaScript 代码babel-loader:把 ES6 转换成 ES5file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去7. 有哪些常见的Plugin?他们是解决什么问题的define-plugin:定义环境变量commons-chunk-plugin:提取公共代码8. Loader和Plugin的不同loader 加载器Webpack 将一切文件视为模块,但是 webpack 原生是只能解析 js 文件. Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力在 module.rules 中配置,也就是说他作为模块的解析规则而存在,类型为数组Plugin 插件扩展 webpack 的功能,让 webpack 具有更多的灵活性在 plugins 中单独配置。类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入9. webpack的构建流程是什么初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译确定入口:根据配置中的 entry 找出所有的入口文件编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果10. 是否写过Loader和Plugin?描述一下编写loader或plugin的思路编写 Loader 时要遵循单一原则,每个 Loader 只做一种"转义"工作。 每个 Loader 的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用 this.callback() 方法,将内容返回给 webpack 。 还可以通过 this.async() 生成一个 callback 函数,再用这个 callback` 将处理后的内容输出出去相对于 Loader 而言,Plugin 的编写就灵活了许多。 webpack 在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果11. webpack的热更新是如何做到的?说明其原理具体可以参考 这里12. 如何利用webpack来优化前端性能压缩代码。删除多余的代码、注释、简化代码的写法等等方式利用 CDN 加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径删除死代码 Tree Shaking)。将代码中永远不会走到的片段删除掉优化图片,对于小图可以使用 base64 的方式写入文件中按照路由拆分代码,实现按需加载,提取公共代码给打包出来的文件名添加哈希,实现浏览器缓存文件13. 如何提高webpack的构建速度参考 这里14. 怎么配置单页应用?怎么配置多页应用单页应用可以理解为 webpack 的标准模式,直接在 entry 中指定单页应用的入口即可多页应用的话,可以使用 webpack 的 AutoWebPlugin 来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范15. 什么是bundle,什么是chunk,什么是modulebundle 是由 webpack 打包出来的文件,chunk 是指 webpack 在进行模块的依赖分析的时候,代码分割出来的代码块。module是开发中的单个模块后记前端发展日新月异,只有保持不断学习的姿态,才能走的更远希望这些面试题可以帮助大家温故知新、查缺补漏,不断充实自己的专业技能,走的更远最后推荐一波 GitHub前端面试题完整版,欢迎小伙伴们 star 关注 不定期更新,期待同行以上 ...

March 29, 2019 · 2 min · jiezi

【手牵手】搭建前端组件库(二)

进阶组件库按需引入在目前,所有的组件会被打包进一个文件,组件库是一骨碌加载完所有组件,同时也会打包和加载多余的代码。对于小项目这样没有问题,但是当组件库越来越庞大、丰富,特别是像我们带业务逻辑的非js库,代码量会更大,如果不管不顾的一通加载完所有资源,后期肯定会带来业务方面的体验问题。所以首要的问题是实现源代码的按需引入,而按需引入的前提是实现源码包按独立组件分割和的拆分打包。代码分拆单个组件独立构建打包的思路就是给打包工具提供多个独立的入口,根据入口收集其所依赖的资源。一个组件入口产出一个文件webpack使用 webpack 配置多入口的方式来按模块拆分打包,每一个模块作为一个入口,与此同时产出对应的文件。webpack 的配置比较简单,不展开.实际构建 library 使用 webpack 有不小的缺点, 应为 webpack 产出后的文件中带有一层包裹代码,这种情况下在碎片化的组件库中反而会使打包体积变大,无法起到‘瘦身’的效果。如下的 webpack 包裹代码:/* 1 //*/ (function(module, webpack_exports, webpack_require) { ‘use strict’; / unused harmony export square / / harmony export (immutable) */ webpack_exports[‘a’] = cube; function square(x) { return x * x; } function cube(x) { return x * x * x; }});哪些额外的代码看着有点不那么清爽.rollup使用 rollup来打包 library,rollup相较于 webpack 在打包体积上会跟小,更加适合 .rollup 的特点:Tree Shaking: 自动移除未使用的代码, 输出更小的文件Scope Hoisting: 所有模块构建在一个函数内, 执行效率更高Config 文件支持通过 ESM 模块格式书写可以一次输出多种格式:模块规范: IIFE, AMD, CJS, UMD, ESMDevelopment 与 production 版本: .js, .min.js是驴是马拉出来溜溜全局安装 rollupnpm install –global rollup// orcnpm install –global rolluprollup 的迭代比较快,这里稍微留意一下 rollup 的版本,部分插件可能不兼容添加rollup.config.js在项目根目录下创建 rollup.config.js 文件:// rollup.config.jsexport default { input: ‘packages/index.js’, output: { file: ’lib/app.all.js’, format: ‘cjs’ }};input:构建入口format:编译打包格式file:编译后输出目录就这么简单,执行以下命令开始将装个 packages 构建构建为一个单文件rollup -c rollup.config.js添加 rollup 多文件构建Rollup 配置文件是一个标准 ES6 模块,默认到处一个对象,也可以到处一个对象用来构建多个模块// rollup.config.jsexport default [{ input: ‘packages/a.js’, output: { file: ’lib/app.a.js’, format: ‘cjs’ }},{ input: ‘packages/b.js’, output: { file: ’lib/app.b.js’, format: ‘cjs’ }}];packages 目录为组件库源码,相关模块不固定,不适宜写死。对于这个问题通过扫描目录获取所有模块,修改 rollup.config.js :// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});function createRollupConfig (file, name) { const config = { input: file, output: { file: lib/index.js : lib/${name}/index.js, format: ’es’, name: name, sourcemap: true } } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;这里打包文件的格式我们使用 es,es是指ES6.这个时候开始构建会报错,因为rollup还不能识别组件库中的 vue 样板代码、语法,同时我们的组件库并不是纯粹的js library, 也需要处理业务组件内引用的样式和图片、字体等。仅仅是使用 rollup 还不能实现我们的目的,需要借助一系列 rollup 插件来完成处理vue.vue文件的编译需要使用rollup-plugin-vue2插件:npm rollup-plugin-vue2 –save-dev处理样式样式处理可以使用 rollup-plugin-css-only插件。由于不喜欢笨拙的css,习惯了scss语法,就直接使用 scss,但其配置就相对要复杂一点。scss样式处理可以使用rollup-plugin-scss插件,为了灵活的处理样式我使用Postcss图片处理html中引入的图片在组件库部署后就无法正常访问了,这里使用 rollup-plugin-url插件将内嵌的图片编译为 base64 再直接放入 js 文件中。对于组件库中有较多大尺寸的图片建议直接将图片放入图片服务器,然后通过url 引入,避免打包文件过大的问题.加入 rollup 插件后的配置:// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);import vue from ‘rollup-plugin-vue2’import postcss from ‘rollup-plugin-postcss’import postcssScss from ‘postcss-scss’import autoprefixer from ‘autoprefixer’import base64 from ‘postcss-base64’import url from ‘rollup-plugin-url’import progress from ‘rollup-plugin-progress’import filesize from ‘rollup-plugin-filesize’;function isDir(dir) { return fs.lstatSync(dir).isDirectory();}const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});function createRollupConfig (file, name) { const config = { input: file, output: { file: lib/${name}/index.js, format: ’es’, name: name, sourcemap: true }, plugins: [ vue(), postcss({ extract: true, parser: postcssScss, plugins: [ base64({ extensions: [’.png’, ‘.jpeg’], root: ‘./packages/’, }), autoprefixer({ add: true }), ] }), url({ limit: 10 * 1024, //include: [’.svg’] }), progress(), filesize() ] } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;到此可以运行 rollup -c rollup.config.js 打包,实现源代码按依赖关系和目录进行分拆打包:lib ├─message │ index.js | index.css | index.js.map ├─pay │ index.js | index.css | index.js.map ├─share │ index.js | index.css | index.js.map打包后的 packages/pay/index.js > lib/pay/index.js :// lib/pay/index.jsvar logo = “data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTk2QkI4RkE3NjE2MTFFNUE4NEU4RkIxNjQ5MTYyRDgiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTk2QkI4Rjk3NjE2MTFFNUE4NEU4RkIxNjQ5MTYyRDgiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjU2QTEyNzk3NjkyMTFFMzkxODk4RDkwQkY4Q0U0NzYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjU2QTEyN0E3NjkyMTFFMzkxODk4RDkwQkY4Q0U0NzYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5WHowqAAAXNElEQVR42uxda4xd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”;var message = {render: function(){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(‘div’,{staticClass:“message”},[_c(’el-row’,{staticClass:“message-test”},[_c(’el-col’,{staticClass:“message-row”,attrs:{“span”:12}},[_c(‘p’,{staticClass:“text”},[_vm._v(“message “+_vm._s(_vm.message)+” – “+_vm._s(_vm.count))])]),_vm._v(” “),_c(’el-col’,{attrs:{“span”:6}},[_c(‘p’,[_vm._v("\n css backgroud image base64\n “)]),_vm._v(” “),_c(‘div’,{staticClass:“tops”}),_vm._v(” “),_c(‘hr’),_vm._v(” “),_c(‘p’,[_vm._v("\n html img 内嵌 image base64\n “)]),_vm._v(” “),_c(‘img’,{staticStyle:{“width”:“100px”},attrs:{“src”:_vm.imgUrl}})])],1)],1)},staticRenderFns: [], name: ‘x-message’, props: { message: String, }, data: function () { return { count: ‘2233hahaha’, imgUrl: logo } }, created: function () { console.log(’logo:’, this.imgUrl); }};message.install = function (Vue) { Vue.component(message.name, message);};export default message;//# sourceMappingURL=index.js.map构建整个组件库在支持按需引入的同时,如果还需支持完整引入整个组件库。则直接在 rollup 配置里加入一个完整的组件库入口rollup.config.js 最终配置// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);const pkg = require(”../package.json”)import vue from ‘rollup-plugin-vue2’import postcss from ‘rollup-plugin-postcss’import postcssScss from ‘postcss-scss’import autoprefixer from ‘autoprefixer’import base64 from ‘postcss-base64’import url from ‘rollup-plugin-url’import progress from ‘rollup-plugin-progress’import filesize from ‘rollup-plugin-filesize’;function isDir(dir) { return fs.lstatSync(dir).isDirectory();}const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});const allScript = ${pkg.name}.allpackages[allScript] = packages/index.js;function createRollupConfig (file, name) { const config = { input: file, output: { file: name === allScript ? lib/index.js : lib/${name}/index.js, format: ’es’, name: name, sourcemap: true }, plugins: [ vue(), postcss({ extract: true, parser: postcssScss, plugins: [ base64({ extensions: [’.png’, ‘.jpeg’], root: ‘./packages/’, }), autoprefixer({ add: true }), ] }), url({ limit: 10 * 1024, //include: [’.svg’] }), progress(), filesize() ] } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;产出的构建目录:lib ├─message │ index.js | index.css | index.js.map ├─pay │ index.js | index.css | index.js.map ├─share │ index.js | index.css | index.js.map index.js //完整组件库,包含所有组件 index.css index.js.map到这里构建部分完成,下一步,将构建后的lib目录发布到 npm:修改package.json version字段与上次不一样(如: 0.1.2),否则会提交失败修改package.json main字段为: lib/index.js发布:执行 npm publish按需引入组件组件库发布后,我们转入业务项目中npm 安装组件库,如:npm i qw-ui安装完成后,在项目node_modules文件夹下可以找到名为_qw-ui@0.1.1@qw-ui,即我们刚才发布的组件库.完整引入当我们想一次引入整个项目,而非单独引入每次组件时: 修改src/main.js,全局引入整个qw-ui,如:import Vue from ‘vue’import App from ‘./App.vue’import ElementUI from ’element-ui’;import ’element-ui/lib/theme-chalk/index.css’;import qwui from ‘qw-ui’ // 全局引入整个组件库import ‘qw-ui/lib/index.css’ // 全局载入样式Vue.config.productionTip = falseVue.use(ElementUI)Vue.use(qwui)new Vue({ render: h => h(App),}).$mount(’#app’)按需引入按需引入需要借助 babel-plugin-import,我们可以只引入需要的组件,以达到减小项目体积的目的.首先npm install babel-plugin-import –save-dev,然后再项目根目录上新建文件.babelrc.vue cli3 直接修改babel.config.js文件:// babel.config.jsmodule.exports = { presets: [ ‘@vue/app’ ], plugins: [[“import”, { “libraryName”: “qw-ui”, “customName”: (name) => { return ../lib/${name}/index; }, “style”: (name) => { return ${name}.css; } }]]}// src/main.jsimport Vue from ‘vue’import App from ‘./App.vue’import ElementUI from ’element-ui’;import ’element-ui/lib/theme-chalk/index.css’;import { message } from ‘qw-ui’ // 按需引入 message 组件Vue.config.productionTip = falseVue.use(ElementUI)Vue.use(message)new Vue({ render: h => h(App),}).$mount(’#app’)这时候已经启用了 babel-plugin-import ,插件会帮我们将import { message } from ‘qw-ui’转换成 import message from ‘qw-ui/lib/message’ 的写法。同时自动注入组件样式。~ 运行一下项目私有npm业务性组件库一般只适合于公司内部,组件或多或少的也涉及到代码安全和商业风险,所以将打包后的组件库发布到私有npm而不是发布到公网上的npm官网相对要安全很多.私有 npm 仓库可以让我们使用组件就像 npm 官方仓库里的包一样方便。私有 npm 仓库有以下一些特性:私有包托管在内部服务器或者单独的服务器上;可以同步整个官方仓库,也可以只同步需要的;下载的时候,可以让公共包走公共仓库,私有包走私有仓库;可以缓存下载过的包;对于下载,发布,有对应的权限管理。私有npm的搭建有多种方式,最简单的使用 git 仓库作为私有仓库.快速搭建和部署私有的 npm 包管理服务也可以使用 verdaccio对权限、安全性、稳定性有更高要求的可以使用 cnpmjs.org, cnpmjs.org 服务的搭建需要配合数据库使用.完! ...

February 26, 2019 · 4 min · jiezi

webpackPlugin插件总结

功能类html-webpack-plugin自动生成html,基本用法:new HtmlWebpackPlugin({ filename: ‘index.html’, // 生成文件名 template: path.join(process.cwd(), ‘./index.html’) // 模班文件})copy-webpack-plugin拷贝资源插件基本用法:new CopyWebpackPlugin([ { from: path.join(process.cwd(), ‘./vendor/’), to: path.join(process.cwd(), ‘./dist/’), ignore: [’*.json’] }])webpack-manifest-plugin && assets-webpack-plugin俩个插件效果一致,都是生成编译结果的资源单,只是资源单的数据结构不一致而已。webpack-manifest-plugin 基本用法:module.exports = { plugins: [ new ManifestPlugin() ]}assets-webpack-plugin 基本用法:module.exports = { plugins: [ new AssetsPlugin() ]}clean-webpack-plugin在编译之前清理指定目录指定内容。基本用法:// 清理目录const pathsToClean = [ ‘dist’, ‘build’] // 清理参数const cleanOptions = { exclude: [‘shared.js’], // 跳过文件}module.exports = { // … plugins: [ new CleanWebpackPlugin(pathsToClean, cleanOptions) ]}compression-webpack-plugin提供带 Content-Encoding 编码的压缩版的资源基本用法:module.exports = { plugins: [ new CompressionPlugin() ]}progress-bar-webpack-plugin编译进度条插件基本用法:module.exports = { //… plugins: [ new ProgressBarPlugin() ]}代码相关类webpack.ProvidePlugin自动加载模块,如 $ 出现,就会自动加载模块;$ 默认为’jquery’的exports用法:new webpack.ProvidePlugin({ $: ‘jquery’,})webpack.DefinePlugin定义全局常量用法:new webpack.DefinePlugin({ ‘process.env’: { NODE_ENV: JSON.stringify(process.env.NODE_ENV) }})mini-css-extract-plugin && extract-text-webpack-plugin提取css样式,对比:mini-css-extract-plugin 为webpack4及以上提供的plugin,支持css chunkextract-text-webpack-plugin 只能在webpack3 及一下的版本使用,不支持css chunk基本用法 extract-text-webpack-plugin:const ExtractTextPlugin = require(“extract-text-webpack-plugin”); module.exports = { module: { rules: [ { test: /.css$/, use: ExtractTextPlugin.extract({ fallback: “style-loader”, use: “css-loader” }) } ] }, plugins: [ new ExtractTextPlugin(“styles.css”), ]}基本用法 mini-css-extract-plugin:const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);module.exports = { module: { rules: [ { test: /.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: ‘/’ // chunk publicPath } }, “css-loader” ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: “[name].css”, // 主文件名 chunkFilename: “[id].css” // chunk文件名 }) ]}编译结果优化类wbepack.IgnorePlugin忽略regExp匹配的模块用法:new webpack.IgnorePlugin(/^./locale$/, /moment$/)uglifyjs-webpack-plugin代码丑化,用于js压缩用法:module.exports = { //… optimization: { minimizer: [new UglifyJsPlugin({ cache: true, // 开启缓存 parallel: true, // 开启多线程编译 sourceMap: true, // 是否sourceMap uglifyOptions: { // 丑化参数 comments: false, warnings: false, compress: { unused: true, dead_code: true, collapse_vars: true, reduce_vars: true }, output: { comments: false } } }] }};optimize-css-assets-webpack-plugincss压缩,主要使用 cssnano 压缩器用法:module.exports = { //… optimization: { minimizer: [new OptimizeCssAssetsPlugin({ cssProcessor: require(‘cssnano’), // css 压缩优化器 cssProcessorOptions: { discardComments: { removeAll: true } } // 去除所有注释 })] }};webpack-md5-hash使你的chunk根据内容生成md5,用这个md5取代 webpack chunkhash。var WebpackMd5Hash = require(‘webpack-md5-hash’); module.exports = { // … output: { //… chunkFilename: “[chunkhash].[id].chunk.js” }, plugins: [ new WebpackMd5Hash() ]};SplitChunksPluginCommonChunkPlugin 的后世,用于chunk切割。webpack 把 chunk 分为两种类型,一种是初始加载initial chunk,另外一种是异步加载 async chunk,如果不配置SplitChunksPlugin,webpack会在production的模式下自动开启,默认情况下,webpack会将 node_modules 下的所有模块定义为异步加载模块,并分析你的 entry、动态加载(import()、require.ensure)模块,找出这些模块之间共用的node_modules下的模块,并将这些模块提取到单独的chunk中,在需要的时候异步加载到页面当中,其中默认配置如下:module.exports = { //… optimization: { splitChunks: { chunks: ‘async’, // 异步加载chunk minSize: 30000, maxSize: 0, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: ‘~’, // 文件名中chunk分隔符 name: true, cacheGroups: { vendors: { test: /[\/]node_modules[\/]/, // priority: -10 }, default: { minChunks: 2, // 最小的共享chunk数 priority: -20, reuseExistingChunk: true } } } }};编译优化类DllPlugin && DllReferencePlugin && autodll-webpack-plugindllPlugin 将模块预先编译,DllReferencePlugin 将预先编译好的模块关联到当前编译中,当 webpack 解析到这些模块时,会直接使用预先编译好的模块。autodll-webpack-plugin 相当于 dllPlugin 和 DllReferencePlugin 的简化版,其实本质也是使用 dllPlugin && DllReferencePlugin,它会在第一次编译的时候将配置好的需要预先编译的模块编译在缓存中,第二次编译的时候,解析到这些模块就直接使用缓存,而不是去编译这些模块。dllPlugin 基本用法:const output = { filename: ‘[name].js’, library: ‘[name]_library’, path: ‘./vendor/’}module.exports = { entry: { vendor: [‘react’, ‘react-dom’] // 我们需要事先编译的模块,用entry表示 }, output: output, plugins: [ new webpack.DllPlugin({ // 使用dllPlugin path: path.join(output.path, ${output.filename}.json), name: output.library // 全局变量名, 也就是 window 下 的 [output.library] }) ]}DllReferencePlugin 基本用法:const manifest = path.resolve(process.cwd(), ‘vendor’, ‘vendor.js.json’)module.exports = { plugins: [ new webpack.DllReferencePlugin({ manifest: require(manifest), // 引进dllPlugin编译的json文件 name: ‘vendor_library’ // 全局变量名,与dllPlugin声明的一致 } ]}autodll-webpack-plugin 基本用法:module.exports = { plugins: [ new AutoDllPlugin({ inject: true, // 与 html-webpack-plugin 结合使用,注入html中 filename: ‘[name].js’, entry: { vendor: [ ‘react’, ‘react-dom’ ] } }) ]}happypack && thread-loader多线程编译,加快编译速度,thread-loader不可以和 mini-css-extract-plugin 结合使用。happypack 基本用法:const HappyPack = require(‘happypack’);const os = require(‘os’);const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });const happyLoaderId = ‘happypack-for-react-babel-loader’;module.exports = { module: { rules: [{ test: /.jsx?$/, loader: ‘happypack/loader’, query: { id: happyLoaderId }, include: [path.resolve(process.cwd(), ‘src’)] }] }, plugins: [new HappyPack({ id: happyLoaderId, threadPool: happyThreadPool, loaders: [‘babel-loader’] })]}thread-loader 基本用法:module.exports = { module: { rules: [ { test: /.js$/, include: path.resolve(“src”), use: [ “thread-loader”, // your expensive loader (e.g babel-loader) “babel-loader” ] } ] }}hard-source-webpack-plugin && cache-loader使用模块编译缓存,加快编译速度。hard-source-webpack-plugin 基本用法:module.exports = { plugins: [ new HardSourceWebpackPlugin() ]}cache-loader 基本用法:module.exports = { module: { rules: [ { test: /.ext$/, use: [ ‘cache-loader’, …loaders ], include: path.resolve(‘src’) } ] }}编译分析类webpack-bundle-analyzer编译模块分析插件基本用法:new BundleAnalyzerPlugin({ analyzerMode: ‘server’, analyzerHost: ‘127.0.0.1’, analyzerPort: 8889, reportFilename: ‘report.html’, defaultSizes: ‘parsed’, generateStatsFile: false, statsFilename: ‘stats.json’, statsOptions: null, logLevel: ‘info’}),stats-webpack-plugin && PrefetchPluginstats-webpack-plugin 将构建的统计信息写入文件,该文件可在 http://webpack.github.io/anal…,并根据分析结果,可使用 PrefetchPlugin 对部分模块进行预解析编译(本人也不理解这个plugin,据说优化效果不明显,有兴趣的同学请见 how-to-optimize-webpacks-build-time-using-prefetchplugin-analyse-tool)。stats-webpack-plugin 基本用法:module.exports = { plugins: [ new StatsPlugin(‘stats.json’, { chunkModules: true, exclude: [/node_modules[\/]react/] }) ]};PrefetchPlugin 基本用法:module.exports = { plugins: [ new webpack.PrefetchPlugin(’/web/’, ‘app/modules/HeaderNav.jsx’), new webpack.PrefetchPlugin(’/web/’, ‘app/pages/FrontPage.jsx’) ];}speed-measure-webpack-plugin统计编译过程中,各loader和plugin使用的时间。const SpeedMeasurePlugin = require(“speed-measure-webpack-plugin”); const smp = new SpeedMeasurePlugin(); const webpackConfig = { plugins: [ new MyPlugin(), new MyOtherPlugin() ]}module.exports = smp.wrap(webpackConfig);转载自:https://segmentfault.com/a/11… ...

February 22, 2019 · 3 min · jiezi

Webpack Bundle Analyzer插件的使用

一、原理读取输出文件夹(通常是dist)中的stats.json文件,把该文件可视化展现的插件。便于直观地比较各个bundle文件的大小,以达到优化性能的目的。二、安装> npm install webpack-bundle-analyzer –save-dev三、作为webpack插件使用(1)引入const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin(2)使用webpackConfig.plugins.push(new BundleAnalyzerPlugin({…}))四、2种方式(1)每次构建时自动打开构建完成之后,浏览器会自动打开localhost:8888,不用改动package.jsonwebpackConfig.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: ‘server’, generateStatsFile: true, statsOptions: { source: false }}))配置参数记得补上,不然构建完不会自动打开~(2)运行特定命令才打开把analyzerMode设置为disabledwebpackConfig.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: ‘disabled’, generateStatsFile: true, statsOptions: { source: false }}))在package.json的scripts字段中加入"bundle-report": “webpack-bundle-analyzer –port 8123 dist/stats.json"其中stat.json文件的位置在打包后的文件夹中,通常是dist,具体情况根据实际情况来定。命令行中键入命令npm run bundle-report浏览器自动打开分析页面:localhost:8123

January 2, 2019 · 1 min · jiezi

细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息

细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息1. js 压缩js 压缩对前端开发者来说是一门必修课。一般来说,压缩 js 主要出于以下两个目的:减小代码体积,加快前端资源加载速度保护源代码不被别人获取压缩 js 使用的工具库:UglifyJS2: 压缩 es5uglify-es: 压缩 es6+closure-compiler、closure-compiler-js: google 的 js 压缩、优化工具压缩 js 的主要过程:移除无用代码混淆代码中变量名称、函数名称等预编译代码对结构进行扁平化处理1. 移除无用代码去掉所有对解析引擎来说无用的字符,包括空格、注释、换行、没有用的变量声明、函数声明等。2. 混淆代码中变量名称、函数名称等把一些局部变量名称、函数名称等用 a, b, …、$1, $2, …、_1, _2, … 之类的简略字符进行替换,达到混淆的目的。源代码(function () { var hello = ‘hi’; var print = function (str) { console.log(str); }; print(hello);})();压缩后的代码(仅演示混淆功能)(function () { var a = ‘hi’; var b = function (c) { console.log(c); }; b(a);})();3. 预编译代码把不依赖外部环境的逻辑提前进行运算,并把运算结果替换到相应的源码处,然后从源码中移除这段逻辑。源代码(function () { var hello = ‘hi’ + ’ everyone, ‘; var count = 3 * 5; console.log(hello + count + ’ girls’);})();压缩后的代码(仅演示预编译功能)(function () { var hello = ‘hi everyone, ‘; var count = 15; console.log(hello + count + ’ girls’);})();4. 对结构进行扁平化处理对于 js 来说,嵌套越深,执行越慢,对代码进行扁平化处理也是优化代码的一种方式。源代码(function () { var say = { hello: function (str) { console.log(‘hello ’ + str); } }; say.hello(’everyone’);})();压缩后的代码(仅演示扁平化结构功能)!function(str){console.log(“hello “+str)}(“everyone”);完整示例源代码(function () { var say = { hello: function (str) { console.log(‘hello ’ + str); } }; say.hello(’everyone’);})();压缩后的代码!function(l){console.log(“hello “+l)}(“50 girls”);2. sourcemap通常 js 压缩后只有一行代码,并且里面的变量名与函数名等都是混淆了的,这在实际运行中会有一个问题,就是 js 的报错信息将会失真,无法追踪到是在源代码哪一行哪一列报的错。sourcemap 便是为了解决这个问题而生的。sourcemap 文件就是记录了从源代码文件到压缩文件的一个代码对应关系记录表,通过压缩文件和 sourcemap 文件可以原原本本找出源代码文件。查看阮一峰老师的 JavaScript Source Map 详解 了解 sourcemap 的原理与格式。一般在压缩 js 的过程中,会生成相应的 sourcemap 文件,并且在压缩的 js 文件末尾追加 sourcemap 文件的链接 //# sourceMappingURL=bundle-file-name.js.map。这样,浏览器在加载这个压缩 js 的时候,就知道还有一个相应的 sourcemap 文件,也一并加载下来,运行的过程中如果 js 报错,也会给出相应源代码的行号与列号,而非压缩文件的。比如,对下面的源码进行压缩:(function () { var say = { hi: function () { console.log(‘hi’); } }; say.hello(); return say;})();未加 sourcemap 文件时,报错信息是:加上 sourcemap 文件时,报错信息是:sourcemap 扩展webpack 对 sourcemap 做了扩展,定义在 devtool 配置项中:eval: 每个模块都使用 eval() 执行,并且都有 //@ sourceURL,构建很快,但无法正确显示行号eval-source-map: 每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中,一般开发模式中使用这种方式cheap-eval-source-map: 类似 eval-source-map,但只映射行,不映射列,并忽略源自 loader 的 source map,仅显示转译后的代码cheap-module-eval-source-map: 类似 cheap-eval-source-map,但会保留源自 loader 的 source mapinline-source-map: source map 转换为 DataUrl 后添加到 bundle 中cheap-source-map: 只映射行,不映射列,并忽略源自 loader 的 source map,仅显示转译后的代码inline-cheap-source-map: inline-source-map 与 cheap-source-map 的结合cheap-module-source-map: 类似 cheap-module-eval-source-map,但不使用 eval() 执行inline-cheap-module-source-map: inline-source-map 与 cheap-module-source-map 的结合source-map: 整个 source map 作为一个单独的文件生成,产品环境一般使用这种模式hidden-source-map: 类似 source-map,但不会把 //# sourceMappingURL=bundle-file-name.js.map 追加到压缩文件后面nosources-source-map: 类似 source-map,但只有堆栈信息,没有源码信息更详细信息可以参考:webpack devtool(英文)webpack devtool(中文)使用建议对于使用 webpack 来构建项目,建议在开发时使用 eval-source-map,产品环境使用 source-map。因为用压缩文件与 sourcemap 文件是可以原原本本的找到源代码的,所以,为了保护源代码,可以这样隐藏 sourcemap 文件:在 web 服务器设置外部不能访问 sourcemap 文件,只能内部访问直接把 sourcemap 文件存放到其他地方3. 通过 sourcemap 查找原始报错信息一般而言,在产品阶段,我们会用 window.onerror 来捕获 js 报错,然后上报到服务器,以此来收集用户使用时发生的 bug:window.onerror = function(message, source, lineno, colno, error) { // message: 错误信息 // source: 报错脚本的 url 地址 // lineno: 行号 // colno: 列号 // error: 错误对象 // 上报必要的信息到服务器}但产品环境的代码都是压缩的,行号和列号都是失真的,所以就需要用 sourcemap 文件来找到错误对应源代码的行号与列号,以及其他的信息。使用工具: mozilla/source-map源代码(function () { var say = { hi: function () { console.log(‘hi’); } }; say.hello(); return say;})();压缩后报错信息window.onerror = function(message, source, lineno, colno, error) { console.log(message: ${message}); console.log(source: ${source}); console.log(lineno: ${lineno}); console.log(colno: ${colno}); console.log(error: ${error});}// message: Uncaught TypeError: e.hello is not a function// source: url/to/bundle.min.js// lineno: 1// colno: 982// error: TypeError: e.hello is not a function通过 source-map 查找原始报错信息const fs = require(‘fs’);const SourceMap = require(‘source-map’);const { readFileSync } = fs;const { SourceMapConsumer } = SourceMap;const rawSourceMap = JSON.parse(readFileSync(‘path/to/js/map/file’, ‘utf8’));SourceMapConsumer.with(rawSourceMap, null, consumer => { const pos = consumer.originalPositionFor({ line: 1, column: 982 }); console.log(pos);});查找到的原始信息{ source: ‘path/to/index.js’, line: 8, column: 7, name: ‘hello’ }这样,便找到了原始报错信息:原始报错文件:path/to/index.js原始报错行号:8原始报错列号:7原始对象名称:hello如此,便能一下子就找到错误在哪里了。更多用法,参考 mozilla/source-map后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) ...

November 12, 2018 · 2 min · jiezi

构建工具是如何用 node 操作 html/js/css/md 文件的

构建工具是如何用 node 操作 html/js/css/md 文件的从本质上来说,html/js/css/md … 源代码文件都是文本文件,文本文件的内容都是字符串,对文本文件的操作其实就是对字符串的操作。操作源代码的方式又主要分成两种:当作字符串,进行增、删、改等操作按照某种语法、规则,把字符串读取成一个对象,然后对这个对象进行操作,最后导出新的字符串1. 操作 html 文件html 的语法比较简单,并且一般操作 html 都是插入、替换、模板引擎渲染等在字符串上的操作,所以使用第一种方式的比较多。比如:html-loaderhtml-webpack-pluginhtml-minifierhandlebars 模板引擎pug 模板引擎ejs 模板引擎一般以第二种方式来操作 html 的都是将 html 文本解析成 dom 树对象,然后进行 dom 操作,最后再导出成新的代码文本。比如:cheeriojsdomparse5以 cheerio 为例,操作 html 文本:cheerio 能够加载一个 html 文本,实例化一个类 jQuery 对象,然后使用 jQuery 的 api 像操作 dom 一样操作这段文本,最后导出新的 html 文本。const cheerio = require(‘cheerio’);const $ = cheerio.load(’<h2 class=“title”>Hello world</h2>’); // 加载一个 html 文本$(‘h2.title’).text(‘Hello there!’);$(‘h2’).addClass(‘welcome’);$.html(); // 导出新的 html 文本//=> <h2 class=“title welcome”>Hello there!</h2>以 jsdom 为例,操作 html 文本:jsdom 是用 js 将一个 html 文本解析为一个 dom 对象,并实现了一系列 web 标准,特别是 WHATWG 组织制定的 DOM 和 HTML 标准。const jsdom = require(“jsdom”);const { JSDOM } = jsdom;const dom = new JSDOM(&lt;!DOCTYPE html&gt;&lt;p&gt;Hello world&lt;/p&gt;);console.log(dom.window.document.querySelector(“p”).textContent); // “Hello world"2. 操作 js 文件因为 js 语法比较复杂,仅仅是如字符串一样进行增删改,只能做一些小的操作,意义不大。所以,一般操作 js 文件都是采用的第二种方式。在第二种方式中,一般是工具将 js 文本解析成抽象语法树(AST,Abstract Syntax Tree,抽象语法树),然后对这棵语法树以面向对象的方式做增删改等操作,最后再导出成新的代码文本。生成抽象语法树的工具主要有:Acorn: 比如 webpack、rollup、UglifyJS 等工具底层都是使用的 acorn 抽象语法树解析器babel-parser: babel 转码工具底层使用的抽象语法树解析器以 acorn 为例,将 1 + 1 片段进行解析:const acorn = require(‘acorn’);const tree = acorn.parse(‘1 + 1’);// tree 的 json 化表示{ type: ‘Program’, start: 0, end: 5, body: [{ type: ‘ExpressionStatement’, start: 0, end: 5, expression: { type: ‘BinaryExpression’, start: 0, end: 5, left: { type: ‘Literal’, start: 0, end: 1, value: 1, raw: ‘1’ }, operator: ‘+’, right: { type: ‘Literal’, start: 4, end: 5, value: 1, raw: ‘1’ } } }], sourceType: ‘script’ }以 babel-parser 为例,将 1 + 1 片段进行解析:const parser = require(’@babel/parser’);const tree = parser.parse(‘1 + 1’);// tree 的 json 化表示{ type: ‘File’, start: 0, end: 5, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, program: { type: ‘Program’, start: 0, end: 5, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, sourceType: ‘script’, interpreter: null, body: [{ type: ‘ExpressionStatement’, start: 0, end: 5, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, expression: { type: ‘BinaryExpression’, start: 0, end: 5, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, left: { type: ‘NumericLiteral’, start: 0, end: 1, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, extra: { rawValue: 1, raw: ‘1’ }, value: 1 }, operator: ‘+’, right: { type: ‘NumericLiteral’, start: 4, end: 5, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 5 } }, extra: { rawValue: 1, raw: ‘1’ }, value: 1 } } }], directives: [] }, comments: [] }3. 操作 css 文件css 的语法比 html 要复杂一些,一些简单的操作如插入、替换,可以用直接以字符串的方式操作,但如果是压缩、auto prefix、css-modules 等复杂的功能时,就需要用第二种方式操作 css 了。在第二种方式中,一般也是将 css 文本解析成一棵抽象语法树,然后进行操作。比如:postcss: 比如 css-loader、autoprefixer、cssnano 等的底层都是使用的 postcss 来解析rework、reworkcss: 抽象语法树解析器csstree: 比如 csso 的底层就是使用 csstree 来解析以 postcss 为例,操作 css 文本:const autoprefixer = require(‘autoprefixer’);const postcss = require(‘postcss’);const precss = require(‘precss’);const css = .hello { display: flex; color: red; backgroundColor: #ffffff;};postcss([precss, autoprefixer({browsers: [’last 2 versions’, ‘> 5%’]})]) .process(css) .then(result => { console.log(result.css); });输出的文本:.hello { display: -webkit-box; display: -ms-flexbox; display: flex; color: red; backgroundColor: #ffffff;}以 rework 为例,操作 css 文本:const css = require(‘css’);const ast = css.parse(‘body { font-size: 12px; }’);console.log(css.stringify(ast));输出的文本:body { font-size: 12px;}4. 操作 markdown/md 文件一般来说,操作 markdown 文本的目的有两个:作为编辑器编辑 markdown 文本,或作为渲染器渲染 markdown 文本为 html 文本从 markdown 文本中读取信息、校验嵌入的源代码、优化格式等所以,尽管 markdown 的语法也很简单,但一般并不会直接去使用字符串的方式去操作 markdown 文本,一般都是使用的第二种方式。比如:markdown-it: 作为编辑器或渲染器的好手remark: 构建抽象语法树进行操作的好手以 markdown-it 为例,操作 markdown 文本:const md = require(‘markdown-it’)();const result = md.render(’# markdown-it rulezz!’);console.log(result);输出的文本:<h1>markdown-it rulezz!</h1>以 remark 为例,操作 markdown 文本:const remark = require(‘remark’)const recommended = require(‘remark-preset-lint-recommended’)const html = require(‘remark-html’)const report = require(‘vfile-reporter’)remark() .use(recommended) .use(html) .process(’## Hello world!’, function(err, file) { console.error(report(err || file)) console.log(String(file)) })校验错误提示:1:1 warning Missing newline character at end of file final-newline remark-lint⚠ 1 warning输出的文本:<h2>Hello world!</h2>后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) ...

November 6, 2018 · 3 min · jiezi

几分钟内提升技能的8个 JavaScript 方法!

我们今天构建的大多数应用程序都需要进行某种数据收集修改。您最常遇到的常见操作是处理集合中的项。不要再使用 for-loop 循环的传统方式( let i=0; i < value.length; i++ )。请注意,在 for 循环中使用 const 会报一个错误。原因是因为它在每次执行期间重新赋值,因此 i 被 i++ 修改。所以每当你想到使用 const 或 let 时,问问自己 – 这个值会被重新声明吗?如果答案是肯定的,请选择 let ,如果不是,请选择 const 。假设您要显示产品列表并对集合进行分类,过滤,搜索,修改或更新。或者您可能希望执行快速计算,例如求和,乘法等。实现这一目标的最佳方法是什么?也许你不喜欢箭头功能,你不想花太多时间学习新东西,或者它们与你无关。放心,并不是只有你这样。我将向您展示如何在 ES5(普通函数)和 ES6(箭头功能)中实现。请注意:箭头函数和函数声明/表达式不是等效的,不能 盲目替换。请记住,this 关键字在两者之间的工作方式不同。我们将要关注的方法有:Spread operator(展开操作符)for…of 迭代器includes()some()every()filter()map()reduce()如果您想成为更好的Web开发人员,开始自己的事业,教别人或提高您的开发技能,我将每周发布最新的关于 Web 开发语言的技术和技巧。1. Spread operator(展开操作符)Spread operator(展开操作符)将数组展开为其对应的元素。它也可以用于对象字面量。为什么我应该用它呢?这是一种简单且快速的方式来列出数组元素同时适用于数组和对象这是一种快速直观的传递参数的方法它只需要三个点 …示例:假如你想展示一个喜爱的水果列表,但不是通过一个循环函数的方式。你可以用一个扩展操作符,像这样:JavaScript 代码:const favoriteFood = [‘Pizza’, ‘Fries’, ‘Swedish-meatballs’];console.log(…favoriteFood);//Pizza Fries Swedish-meatballs2. for…of 迭代器for…of 语句循环/遍历集合,为你提供了修改特定元素的功能。 它取代传统的 for-loop 方式。为什么我应该用它呢?这是添加或更新项的简单方法执行计算(求和,乘法等)和条件语句结合使用(if,while,switch等)代码干净,可读性高示例:假设你有一个工具箱,你想要展示里面所有的工具。for…of 迭代器会让它变得更简单。JavaScript 代码:const toolBox = [‘Hammer’, ‘Screwdriver’, ‘Ruler’]for(const item of toolBox) {console.log(item)}// Hammer// Screwdriver// Ruler3. Includes() 方法include() 方法被用来检查数集合中是否包含指定的元素,如果存在则返回 true,否则返回 false。 请记住,它是区分大小写的:如果集合中的某项元素是 SCHOOL,并且但你查询的是 school ,那么它将会返回 false。为什么我应该用它呢?构建简单的搜索功能这是一种确定元素项是否存在的直观方法它使用条件语句来修改,过滤等代码可读性高示例:比如,无论出于什么原因,你不知道车库里有什么车,你需要一个系统检查你想要的车是否在车库里。includes() 可以拯救你!JavaScript 代码:const garge = [‘BMW’, ‘AUDI’, ‘VOLVO’];const findCar = garge.includes(‘BMW’);console.log(findCar);// true4. Some() 方法some() 方法检查在数组中是否存在某些元素,如果存在返回true,否则返回 false 。这和 includes()方法相似,但是 some() 方法的参数是一个函数,而不是一个字符串。为什么我应该用它呢?它确保某些项目通过测试它使用函数执行条件语句使您的代码更具声明性它足够好用示例:假如你是一个俱乐部老板,并不在乎谁进入这俱乐部吧。但是有些人是不允许进来的,因为他们已经喝了很多酒(我的想象力很好)。查看以下 ES5 和 ES6 之间的差异:ES5:JavaScript 代码:const age = [16, 14, 18];age.some(function(person) {return person >= 18;});// Output: trueES6:JavaScript 代码:const age = [16, 14, 18];age.some((person) => person >= 18);// true5. Every() 方法every() 方法循环遍历数组,检查数组中的每个元素项,并返回 true 或 false 。与 some() 概念相似。但是每一项都必须通过条件表达式,否则返回 false 。为什么我应该用它呢?它确保每个项目都通过测试您可以使用函数执行条件语句使您的代码更具声明性示例:上次你用 some()方法允许一些未成年学生进入俱乐部,有人举报了这事,警察抓住了你。这次你不会让这种情况再次发生,你将用 every() 方法确保每一个进来俱乐部的人都是满足年龄限制的。ES5:JavaScript 代码:const age = [15, 20, 19];age.every(function(person) {return person >= 18;})// Output: falseES6:JavaScript 代码:const age = [15, 20, 19];age.every((person)=> person >= 18);// false6. Filter() 方法filter() 方法创建一个包含所有通过测试的元素的新数组。为什么我应该用它呢?你可以修改原始数组你可以让你过滤掉那些你不需要的元素项让你的代码可读性更高示例:假如你只想要大于或者等于30的价格,过滤掉其他价格。ES5:JavaScript 代码://arrayconst prices = [25, 30, 15, 55, 40, 10];prices.filter(function(price){return price >= 30;})// Output: [30, 55, 40]ES6:JavaScript 代码:const prices = [25, 30, 15, 55, 40, 10];prices.filter((price) => price >= 30);// [30, 55, 40]7. Map() 方法在返回新数组方面,map() 方法跟 filter() 方法相似。但是,唯一的区别是它用于修改数组中的元素项。为什么我应该用它呢?它可以让你避免对原始数组进行修改你可以修改你所需的元素项代码可读性更高示例:假如你有一份价格清单。 您的经理需要一个清单,以便展示在税率增加25%后的新价格。 map()方法可以帮助你。ES5:JavaScript 代码:const productPriceList = [200, 356, 1500, 5000];productPriceList.map(function(item){return item * 0.75;})// [150, 267, 1125, 3750]ES6:JavaScript 代码:const productPriceList = [200, 356, 1500, 5000];productPriceList.map((item) => item * 0.75);// [150, 267, 1125, 3750]8. Reduce() 方法reduce()方法可用于将数组转换为其他内容,可以是整数,对象,promises 链(顺序执行的promises)等等。出于实际原因,一个简单的用例是对整数列表求和。简而言之,它将整个数组“缩短”为一个你想要的值。为什么我应该用它呢?执行计算计算一个值计算重复数按属性分组对象按顺序执行promises这是一种快速执行计算的方法示例:假如你想得到这一周的消费总和,reduce()可以帮你实。ES5:JavaScript 代码:const weeklyExpenses = [200, 350, 1500, 5000, 450, 680, 350]weeklyExpenses.reduce(function(first, last) {return first + last;})// 8530ES6:JavaScript 代码:const weeklyExpenses = [200, 350, 1500, 5000, 450, 680, 350]weeklyExpenses.reduce((first, last) => first + last);// 8530希望你的 JavaScript 技能有所提升!原文链接:https://www.jianshu.com/p/22f… ...

November 4, 2018 · 2 min · jiezi

如何构建大型的前端项目

如何构建大型的前端项目1. 搭建好项目的脚手架一般新开发一个项目时,我们会首先搭建好一个脚手架,然后才会开始写代码。一般脚手架都应当有以下的几个功能:自动化构建代码,比如打包、压缩、上传等功能本地开发与调试,并有热替换与热加载等功能本地接口数据模拟css 模块化检查并自动矫正不符合规范的代码,并优化代码格式社区已有很多模板(boilerplate)或工具帮助我们搭建一个现成的脚手架,比如:create-react-appvue-cliyeomanhtml5-boilerplatereact-boilerplatehackathon-starter当然,如果这些模板或工具都不能满足你的需求,你也可以搭建自己的脚手架,可以参考:搭建自己的前端脚手架怎样提升代码质量CSS 模块化css 的弱化与 js 的强化本地化接口模拟、前后端并行开发2. 定义好项目的目录结构对于大型目录而言,一个好的目录结构是非常必要的。一个好的目录结构应当具有以下的一些特点:解耦:代码尽量去耦合,这样代码逻辑清晰,也容易扩展分块:按照功能对代码进行分块、分组,并能快捷的添加分块、分组编辑器友好:需要更新功能时,可以很快的定位到相关文件,并且这些文件应该是很靠近的,而不至于到处找文件可以参考 目录结构优化3. 做好项目的组件化这里的组件化,并非框架层面的组件化,而是工程层面的组件化。当一个项目中的不同的区块需要共用同一段代码,或者不同项目之间需要共享同一个组件的时候,就需要做组件化了。组件化一般分为项目内的组件化和项目外的组件化。项目内的组件化是,同一个项目内不同区块共用的代码可以提取出来为一个单独的组件。项目外的组件化是,跨项目间共享的代码可以提取出来为一个单独的项目,然后如正常的 npm 包一样使用。可以参考:组件化私有 npm 仓库4. 封装团队的构建工具对于多项目而言,不管使用 webpack 还是 rollup 来打包项目,都会面临一些问题:不同项目有不同配置文件,更新配置需要到处改,而且很难保持一致如果有项目的升级,不同区块可能会有不同的配置这个时候,把配置文件封装成一个项目,然后使用版本化的方式管理,如正常的 npm 包一样使用,就会很方便了。比如,我的项目都是使用 lila 来构建的。5. 选好基础 js 框架、ui 框架、页面模板开始开发前,需要先选好基础 js 框架,比如 react、vue、angular、jquery 等,因为一旦选定,基本上后面都不能更换了,因为更换的成本太大了。选好基础 js 框架后,可以选一个比较好的 ui 框架,这样可以少写很多代码,可以参考 前端最受欢迎的 UI 框架。如果页面的定制化程度不高,可以选择一个比较好的页面模板,比如:AdminLTEvue-element-admingentelellangx-adminant-design-pro6. 测试一般前端测试分以下几种:单元测试:模块单元、函数单元、组件单元等的单元块的测试集成测试:接口依赖(ajax)、I/O 依赖、环境依赖(localStorage、IndexedDB)等的上下文的集成测试样式测试:对样式的测试E2E 测试:端到端测试,也就是在实际生产环境测试整个应用一般会用到下面的一些工具:jestenzymeseleniumpuppeteer另外,可以参考 聊聊前端开发的测试。7. 持续集成构建与测试一般大型工程的的构建与测试都会花很长的时间,在本地做这些事情的话就不太实际,这就需要做持续集成构建与测试了。持续集成工具用的比较多的:jenkinsgitlab cijenkins 是通用型的工具,可以与 github、bitbucket、gitlab 等代码托管服务配合使用,优点是功能强大、插件多、社区活跃,但缺点是配置复杂、使用难度较高。gitlab ci 是 gitlab 内部自带的持续集成功能,优点是使用简单、配置简单,但缺点是不及 jenkins 功能强大、绑定 gitlab 才能使用。另外,如果是开源项目,travis ci 是个不错的选择。后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

October 11, 2018 · 1 min · jiezi

使用 ng-packagr 打包 Angular

写在前面为了让 Angular 类库应用范围更自由,Angular 提出一套打包格式建议名曰:Angular Package Format,包括 FESM2015、FESM5、UMD、ESM2015、ESM5、ES2015 格式,不同格式可以在不同的环境(Angular Cli、Webpack、SystemJS等)中使用。传统方式需要对这些格式逐一打包,一个示例打包脚本写法。这种写法只能针对不同项目的配置,而且除非你了解这些格式的本质否则很难维护;后来社区根据 APF 规范实现了类库 ng-packagr,通过简单的配置可以将你的类库打包成 APF 规范格式。至 V6 以后 Angular Cli 也基于 ng-packagr 实现了另一个 @angular-devkit/build-ng-packagr 应用构建器。如何使用既然 ng-packagr 被 Angular Cli 内置,这让我们进一步简化了生产一个 APF 规范格式的类库的成本。在 Angualr Cli 里使用 ng g library 来创建一个类库模板,例如在一个新的 Angular 应用里执行:ng g library <library name>而打包,则:ng build <library name>最终,将生成的 dist/<libary name> 目录下文件上传相应包管理服务器(例如:npm)提供给其他 人使用。配置说明由 Angular Cli 生成的类库模板大部分内容同 Angular 应用一样,只是多了一个 ng-package.json 的配置文件(对于生产环境是 ng-package.prod.json),它是专门针对 ng-packagr 的一个配置文件,如同 angular.json 一般也是基于 JSON Schema 格式,因此可以通过访问 ng-package.schema.json 了解所有细节,以下描述一些重点项。whitelistedNonPeerDependenciesng-packagr 默认会根据 package.json 的 peerDependencies 节点清单来决定类库所需要第三方依赖包,这些依赖包是不会被打包至类库。然而,所依赖包不存在 peerDependencies 节点里时(当然建议需要依赖的项应该在里面),就需要该属性的配置。lib/entryFile指定入口文件。lib/umdModuleIdsUMD 格式采用 rollup 打包,当类库需要引用一些无法猜出正确 UMD 标识符时,就需要你手动映射这些类库的标识。“umdModuleIds”: { “lodash”: “_"}angular.jsonAngular Cli 配置文件 angular.json 内会增加一个以 <libary name> 命名的构建配置,绝大多数配置性同普通 Angular 应用如出一辙,唯一不同的是 builder 节点为:“builder”: “@angular-devkit/build-ng-packagr:build"次级入口有时候一个类库可能会包含着多个二次入口,就像 @angular/core 类库包含着一个 @angular/core/testing 模块,它只是运用于测试,因此并不希望在项目中引入 @angular/core 时也包含测试代码,但同时二者又是同一个功能性时,这种次级导入显得非常重要。另一种像 ngx-bootstrap、@angular/cdk/ally 等都提供次级模块的导入,可以更好的优化体积。不论出于何种目的,都可以通过 Angular Cli 简单的文件组织进一步打包出主、次级分明的类库。ng g library 生成的结构大概如下:<libary name>├── src| ├── public_api.ts| └── lib/.ts├── ng-package.json├── ng-package.prod.json├── package.json├── tsconfig.lib.json└── tsconfig.spec.json当根目录下包含 README.md、LICENSE 时会自动被复制到 dist 目录中,Npm 规定必须包含 README.md 文件,否则访问已发布类库页时会有未找到描述文件错误提示。若想创建一个 <libary name>/testing 的次级入口,只需要在 <libary name> 根目录下创建一个 testing 目录:<libary name>├── src| ├── public_api.ts| └── lib/.ts├── ng-package.json├── ng-package.prod.json├── package.json├── tsconfig.lib.json├── tsconfig.spec.json└── testing ├── src | ├── public_api.ts | └── .ts └── package.json核心是需要提供一个 package.json 文件,而且内容简单到姥姥家。{ “ngPackage”: {}}最后,依然使用 ng build <libary name>,会产生一个次级导入模块。小结至此,基本上利用 Angular Cli 可以快速的构建一个可发布于 Npm Angular 类库,更复杂的可以构建像 ngx-bootstrap、@angular/cdk/ 类库。自定义构建Angular Cli 虽然提供非常便利的环境,但是对于一些复杂环境像 Delon 类库(ng-alain基建系列类库)包含着多个类库、类库又包含多个次级导入时,Angular Cli 会显得有点啰嗦,特别是对每个类库的 angular.json 配置。其实 @angular-devkit/build-ng-packagr 非常简单,如果将取进一步简化,整个实现差不多相当于:const path = require(‘path’);const ngPackage = require(’ng-packagr’);const target = path.resolve(__dirname, ‘./projects/<libary name>’);ngPackage .ngPackagr() .forProject(path.resolve(target, ng-package.prod.json)) .withTsConfig(path.resolve(target, ’tsconfig.lib.json’)) .build() .then(() => { // 构建完成后干点事 });将上面的代码放到 ./build.js,执行:node scripts/build.js其结果完成是等价。build() 返回的是一个 Promise 对象,意味着可以确保构建开始前和结束后做一点额外的事。总结ng-packagr 极大简化 Angular 类库被打包出一个 APF 规范建议,虽然它以 ng- 开头,但本质上并不一定非要在 Angular 中运用,也可以使用在 React、VUE。 ...

September 20, 2018 · 2 min · jiezi