关于vue-cli3:vuecli3elementplus-从菜单渲染浅谈动态渲染Vue组件的问题

1. 对于Element-plus的菜单渲染问题跨过了Element-UI,终于来到了Element-plus。又回到了一个老问题,menu的渲染。创立一个menu数组,利用v-for来渲染数组,生成menu,十分惯例的操作。然而操作的过程中,呈现了一个小问题,就是对于icon的渲染。咱们晓得,在Element-plus中,渲染一个带图标的菜单项,是这么搞的: <el-menu-item index="/mypath"> <template #title> <el-icon><Odometer /></el-icon> <span>title</span> </template></el-menu-item>icon图标是间接以一个组件的模式进行渲染的。那么,当咱们希图利用v-for进行列表渲染的时候,这个图标的组件怎么渲染进去,成了个难题。间接用双花括号{{}}必定是不行的,间接会把标签搞成文本。用v-html也不行,它只能渲染原生的HTML标签。WTF? 2. 如何能力动静的把自定义组件渲染进去?在<template></template>外面搞模版语法是行不通了。那就只能尝试走其余的路线了。在搜索引擎欢快的与海量信息格斗之后,找到了切入点:render函数。诚实说,其实早就该想到这个了,毕竟组件渲染就这么两条路嘛。奈何对render的应用频率太低了,选择性的搞遗记了。那么来尝试吧。写一个组件,通过props接管到图标的标签写法,而后渲染进去。 //留神在vue3中,render函数中不再有参数了,h函数须要按需加载。import { h } from 'vue';export default{ props: { //Odometer html: String }, render(){ return h('el-icon', null, h(this.html)); }}果不其然没有达到成果。罕用vue做开发的小伙伴必定一眼就发现了一个问题:用h函数生成虚构DOM节点时,如果要生成的是组件,则第一个参数间接应用导入的组件即可。如果应用字符串,会一成不变的把字符串当做HTML标签渲染,而不是当作组件渲染。(参考链接) 批改一下: import { h } from 'vue';import { ElIcon } from 'element-plus';export default{ props: { //Odometer html: String }, components: { ElIcon }, render(){ return h(ElIcon, null, h(this.html)); }}还是不对呀,图标名称是传过来的字符串,没法间接获取到导入的组件呀。吓得我连忙又翻了一下文档,在最初一行找到了这么一句话: 如果一个组件是用名字注册的,不能间接导入 (例如,由一个库全局注册),能够应用 resolveComponent() 来解决这个问题。原来如此。。。好了,给出最终答案: <el-menu-item :index="item.path"> <template #title> <DynamicIcon :html="item.icon"></DynamicIcon> <span>{{item.title}}</span> </template></el-menu-item>//DynamicIconimport { h, resolveComponent } from 'vue';import { Odometer, ChatDotRound } from '@element-plus/icons-vue';export default{ props: { //Odometer html: String }, components: { Odometer, ChatDotRound }, render(){ //ElIcon间接全局全局导入了 const IconBox = resolveComponent('ElIcon'); const Icon = resolveComponent(this.html); return h(IconBox, null, h(Icon)); }}3. 总结最初总结一下子吧。想要动静渲染组件,就须要利用props与render函数。在应用h函数的时候,生成组件的虚构vnode,要间接应用导入的组件。如果只能获取一个组件名称,那么就用resolveComponent函数手动解析注册的组件。 ...

November 8, 2022 · 1 min · jiezi

关于vue-cli3:基于-Vue3-打造前台中台通用提效解决方案完整无密

download:基于 Vue3 打造前台+中台通用提效解决方案残缺无密揭秘 Kotlin 1.6.20 重磅功能 Context Receivers 这篇文章咱们一起来聊一下 Kotlin 1.6.20 的新功能 Context Receivers,来看看它为咱们解决了什么问题。 通过这篇文章将会学习到以下内容: 扩大函数的局限性什么是 Context Receivers,以及如何使用Context Receivers 解决了什么问题引入 Context Receivers 会带来新的问题,咱们如何解决Context Receivers 利用范畴及注意事项扩大函数的局限性在 Kotlin 中承受者只能利用在扩大函数或者带承受者 lambda 表达式中, 如下所示。 class Context { var density = 0f}// 扩大函数inline fun Context.px2dp(value: Int): Float = value.toFloat() / density复制代码承受者是 fun 关键字之后和点之前的类型 Context,这里藏匿了两个学识点。 咱们可能像调用外部函数一样,调用扩大函数 px2dp(),通常拆散 Kotlin 作用域函数 with , run , apply 等等一起使用。with(Context()) { px2dp(100)}复制代码在扩大函数外部,咱们可能使用 this 关键字,或者藏匿关键字隐式拜访外部的成员函数,然而咱们不能拜访公有成员扩大函数使用起来很便利,咱们可能对系统或者第三方库进行扩大,然而也有局限性。 只能定义一个承受者,因此限度了它的可组合性,如果有多个承受者只能当做参数传送。比如咱们调用 px2dp() 方法的同时,往 logcat 和 file 中写入日志。class LogContext { ...

May 12, 2022 · 2 min · jiezi

关于vue-cli3:antd-vue-在-vueconfigjs中-自定义主题颜色定制主题vuecli3

我在 自定义less变量 时,less、less-loader的版本号不同,会报奇怪的谬误此篇,定制主题 也会依据版本号报不同的谬误最终!我选用的版本 less@3.0.4、less-loader@5.0.0这篇也基于 less@3.0.4 、less-loader@5.0.0 进行定制主题操作一、vue.config.js文件module.exports = { // 减少这部分代码 css: { requireModuleExtension: true, loaderOptions: { less: { modifyVars: { // less vars,customize ant design theme 'primary-color': '#6F48FF', 'link-color': '#6F48FF' }, // do not remove this line javascriptEnabled: true } } } }下面的 modifyVars 参数能够自定义各种主题色,以下是罕用的几种: @primary-color: #1890ff; // 全局主色@link-color: #1890ff; // 链接色@success-color: #52c41a; // 胜利色@warning-color: #faad14; // 正告色@error-color: #f5222d; // 谬误色@font-size-base: 14px; // 主字号@heading-color: rgba(0, 0, 0, 0.85); // 题目色@text-color: rgba(0, 0, 0, 0.65); // 主文本色@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色@disabled-color: rgba(0, 0, 0, 0.25); // 生效色@border-radius-base: 4px; // 组件/浮层圆角@border-color-base: #d9d9d9; // 边框色@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层暗影也能够点击查看 所有款式变量 ...

November 30, 2021 · 1 min · jiezi

关于vue-cli3:自定义Vuecli项目模板

模板构造次要包含四个局部: preset.jsonprompts.jsgenerator/index.jstemplate/preset.jsonpreset.json 中是一个蕴含创立新我的项目所需预约义选项和插件的 JSON 对象,让用户无需在命令提醒中抉择它们,简称预设; prompts.js交互式的告知vue create所需,是依据用户应用需要自定义设置的信息。 定义格局参考Inquirer 问题格局的数组(Inquirer官网文档) generator.jsgenerator.js 导出一个函数,这个函数接管三个参数 一个 GeneratorAPI 实例 提供一系列的API管制最终输入的目录构造及内容自定义模版必然用到 GeneratorAPI 的 render() 办法用户自定义的设置选项 即:用户对 prompts.js 中问题所提供的答案整个 preset 预设信息简略的自定义模板示例创立我的项目手动创立目录构造: |- vue-template |- generator |- index.js |- preset.json |- prompts.js获取preset.json模板先用 vue create 去创立一个我的项目,而后把你的预设信息保留下来,到指定目录下查找预设信息: # Unix~/.vuerc# windowsC://用户/<username>/.vuerc{ "useTaobaoRegistry": false, "latestVersion": "4.5.14", "lastChecked": 1634820758861, "packageManager": "npm", "presets": { "v2": { "useConfigFiles": true, "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-typescript": { "classComponent": false, "useTsWithBabel": true }, "@vue/cli-plugin-router": { "historyMode": false }, "@vue/cli-plugin-vuex": {}, "@vue/cli-plugin-eslint": { "config": "prettier", "lintOn": [ "save", "commit" ] } }, "vueVersion": "2", "cssPreprocessor": "dart-sass" } }}其中,presets 保留的就是预设信息,v2 是保留预设起的别名,咱们的preset.json 须要的就是 v2 的值,所以preset.json 中的内容就是这样 ...

October 26, 2021 · 2 min · jiezi

关于vue-cli3:vuecli-elementui-环境变量与模式

1.环境变量的设置vue-cli3中,通过在根目录下设置.env[.mode]文件,来设置环境变量。在文件中,通过 键=值 设置不同的环境变量。 //.envVUE_APP_API_BASEURL = 'a.com'But 只有以VUE_APP_结尾的变量,才会被webpack.DefinePlugin动态嵌入到客户端侧的包中,作为全局变量应用。应用形式为: let api = process.env.VUE_APP_API_BASEURL;2.模式通过传递 --mode 选项参数为命令行覆写默认的模式。 //package.json{ "scripts": { "dev-build": "vue-cli-service build --mode development" }}能够通过为 .env 文件减少后缀来设置某个模式下特有的环境变量。比方在development模式下,只会加载文件env.development内的环境变量。 3.常见应用场景切换环境对应的api接口。比方,在.env.development文件中设置测试环境api的根底门路,在.env.production文件中设置生产环境api的根底门路。这样,通过应用--mode给命令行设置不同的模式,就能够让打包好代码在不同环境下切换api根底门路。

December 2, 2020 · 1 min · jiezi

关于vue-cli3:vuecli3运行npm-run-serve报错

vuecli3运行npm run serve报错1.开发环境 vuecli32.电脑系统 windows10专业版3.在应用vuecli3开发的过程中,咱们输出 npm run serve可能会遇到一报错,上面我来分享一下我遇到的问题和解决办法,心愿对你有所帮忙。4.输出 npm run serve 报错如下:4-1.看到以上报错,在网上查阅了大量的材料还是无奈解决,于是,开始本人尝试去剖析以及解决,我的项目在运行的时候,会先执行 vue.config.js,如果你的这个文件配置有问题的话,我的项目就无奈运行,首先查看 vue.config.js,代码如下: 刚开始看的时候,你会感觉没有问题,然而在这里咱们的一个配置被正文了,就是 target ,当咱们把这个配置正文开之后,成果如下:5.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰~!

November 17, 2020 · 1 min · jiezi

关于vue-cli3:vuecli3使用本地彩色icon

vuecli3应用本地黑白icon1.开发环境 vuecli32.电脑系统 windows10专业版3.在应用vuecli3开发的过程中,咱们常常会应用到 icon,然而应用失常的办法,你会发现引入的图标是没有色彩的,上面我来分享一下应用办法,心愿对你有所帮忙!4.将图标下载到本地5.删除红框局部5-1:除了以下这些文件,其余的文件都能够删除6.把其余文件,复制到本人的我的项目中,在assets中新建文件并且把方才拷贝的文件放到新建目录下,构造如下:7.在main.js中增加如下代码: import './assets/chenicon/iconfont.css';import './assets/chenicon/iconfont.js';//进行全局导入,这样我的所以组件中都能够应用;如果整个我的项目只有一个页面应用了,能够单个页面进行导入//留神:如果应用黑白图标,须要引入 iconfont.js文件8.在vue组件中应用办法,增加如下代码: <svg class="icon" aria-hidden="true"> <use xlink:href="#icongouwuche"></use></svg><svg class="icon" aria-hidden="true"> <use xlink:href="#iconyemian-copy"></use></svg>//留神: href 中 # 前面就是 icon 类名8-1:在App.vue增加如下代码: .icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden;}9.成果如下: //比照,咱们 阿里巴巴矢量图我的项目中的图标/色彩 截然不同。10.本期的教程到了这里就完结啦,是不是很nice,心愿对你有所帮忙,让咱们一起致力走向巅峰!

November 14, 2020 · 1 min · jiezi

关于vue-cli3:PC端只对指定页面做自适应vuepostcsspxtorem

一、postcss-pxtorem装置npm install -S postcss-pxtorem二、rem 配置## rem.jsfunction setRem() { `// 基准大小 const baseSize = 100; const baseScale = baseSize / 1920; // 1920的设计图 let widthScale = window.innerWidth; // 以后窗口的宽度 const heightScale = window.innerHeight; // 以后窗口的高度 // 尺寸换算 const comparedHeight = (widthScale * 1080) / 1920; if (heightScale < comparedHeight) { widthScale = (heightScale * 1920) / 1080; } // 计算理论的rem值,失去该宽度下的相应font-size值,并赋予给html的font-size, const rem = widthScale * baseScale; document.documentElement.style.fontSize = ${rem}px;}// 初始化setRem();// 扭转窗口大小时从新设置 remwindow.onresize = () => { setRem();};`三、在main.js外面引入import './utils/rem';四、配置postcss-pxtorem### 根目录的postcss.config.js`module.exports = { plugins: { autoprefixer: {}, 'postcss-pxtorem': { rootValue: 100, minPixelValue: 12, propList: ['*'], exclude: (e) => { if (/src(\\|\/)views(\\|\/)Screen/.test(e)) { return false; } return true; }, }, },};`五、疏忽单个属性的形式(不转换rem)1、配置propList: ['*', '!border', '!font-size'], 2、像素单元申明中应用大写 .rank-list__text { width: 300Px; }六、参考文档1、https://www.npmjs.com/package/postcss-pxtorem2、https://www.njleonzhang.com/2018/08/15/flexible-pc-full-screen.html3、https://www.cnblogs.com/WhiteM/p/12720849.html

November 13, 2020 · 1 min · jiezi

关于vue-cli3:vuecli3配置postcssloader使用BEM

网上找了很多文章,但也没看到几个是实用的,而且很多是2017年左右的文章,用到的技术postcss-cssnext曾经被弃用,目前应用postcss-preset-env替换,但这个插件不欠缺,或者是我没找到解决办法,存在一部分问题,但应用BEM写法是没什么问题的。BEM写法示例:<template> <div class="ga-home__container"> ... </div></template><style> @component-namespace ga { @b home { @e container { width: 100%; height: 100%; color: #a2eeff; } }} </style>vue2.x配置BEM应用到插件postcss-salad进行配置;在目录.postcssrc.js,配置postcss-salad即可; // 应用的版本号// "postcss": "^5.2.17",// "postcss-salad": "^1.0.8",// .postcssrc.jsvar salad = require('postcss-salad')module.exports = { "plugins": [ // to edit target browsers: use "browserlist" field in package.json salad({ browser: ['ie > 9', 'last 2 version'], features: { 'bem': { 'shortcuts': { 'component': 'b', 'modifier': 'm', 'descendent': 'e' }, 'separators': { 'descendent': '__', 'modifier': '--' } } } }) ]}vue-cli3配置BEM写法应用到的插件postcss-bem-fix、postcss-preset-env;【留神】:postcss-bem比方旧版,目前舍不得postcss-bem-fix能够查看以下文章:《postss-bem version is too low》《弃用cssnext》 ...

November 5, 2020 · 1 min · jiezi

关于vue-cli3:vuecli3搭建项目

一、nodejs装置和淘宝镜像装置下载网站:https://nodejs.org/en/ (一路next即可) 查看node 版本 node -v永恒应用淘宝镜像命令: npm config set registry https://registry.npm.taobao.org二、vue-devtools 装置及应用vue-devtools是一款基于chrome浏览器的插件,用于调试vue利用,这能够极大地提高咱们的调试效率。接下来咱们就介绍一下vue-devtools的装置。(1)chrome商店间接装置vue-devtools能够从chrome商店间接下载安装 (2)手动装置① 找到vue-devtools的github我的项目,并将其clone到本地. vue-devtoolsgit clone https://github.com/vuejs/vue-devtools.git② 装置我的项目所须要的npm包npm install③ 编译我的项目文件npm run build④ 增加至chrome浏览器浏览器输出地址"chrome://extensions/"进入扩大程序页面,点击"加载已解压的扩大程序..."按钮,抉择vue-devtools>shells下的chrome文件夹。 如果看不见“加载已解压的扩大程序...”按钮,则须要勾选“开发者模式”。 三、vue-cli 装置及应用(1) 装置vue-clinpm install @vue/cli -g(2) 创立一个我的项目vue create <Project Name>` `//文件名 不反对驼峰(含大写字母)具体操作如下:1.抉择一个preset(预设)① 除最初两个选项,其余选项都是你之前保留的预设配置(如下图第一个 " my-default " 是之前保留的预设配置,当初就能够间接用了) ② 如果没有配置保留过,则只有以下两个选项: default(babel,eslint):默认设置(间接enter)适宜疾速创立一个新我的项目的原型,没有带任何辅助性能的 npm包Manually select features:自定义配置(按方向键 ↓)是咱们所须要的面向生产的我的项目,提供可选性能的 npm 包 2.自定义配置须要抉择你须要的配置项 ? Check the features needed for your project: >( ) Babel //转码器,能够将ES6代码转为ES5代码,从而在现有环境执行。( ) TypeScript// TypeScript是一个JavaScript(后缀.js)的超集(后缀.ts)蕴含并扩大了 JavaScript 的语法,须要被编译输入为 JavaScript在浏览器运行,目前较少人再用( ) Progressive Web App (PWA) Support// 渐进式Web应用程序( ) Router // vue-router(vue路由)( ) Vuex // vuex(vue的状态管理模式)( ) CSS Pre-processors // CSS 预处理器(如:less、sass、stylus)( ) Linter / Formatter // 代码格调检查和格式化(如:ESlint)( ) Unit Testing // 单元测试(unit tests)( ) E2E Testing // e2e(end to end) 测试3. 抉择对应性能的具体工具包① 是否应用history router ...

August 18, 2020 · 2 min · jiezi

关于vue-cli3:解决Vue-Cli-3vuecli运行项目eslint报错nooctalescapejs41

明天应用vue cli 3来搭建vue环境。同时vue ui第一次应用图形界面创立我的项目,一切顺利,我的项目创立胜利。npm run serve...呈现了下图报错:(node_moduleseslintlibrulesno-octal-escape.js:41)度到第一篇解决办法《vue-cli工程启动异样问题:no-octal-escape.js:41》,依照其说的第二种办法,升高了eslint版本,再次运行我的项目,报错仍旧。接着又第二篇解决办法,临时敞开了eslint,躲避了报错《新版vue脚手架敞开eslint》须要在我的项目根目录下新建一个vue.config.js module.exports = { lintOnSave: false}

August 13, 2020 · 1 min · jiezi

关于vue-cli3:vuecli3-libflexiblepostcsspxtorem-适配pc端大屏分辨率

vue-cli3脚手架 lib-flexible+postcss-pxtorem 适配pc端大屏分辨率我的我的项目是cli3构建,iviewUI框架,UI设计稿是1920*10801.装置lib-flexible postcss-pxtorem 插件 npm install lib-flexible postcss-pxtorem --save2.在 main.js 文件中引入 import 'lib-flexible/flexible.js'3.新建 postcss.config.js 或者在package.json中写入 module.exports = { plugins: { autoprefixer: {}, "postcss-pxtorem": { "rootValue": 192,// 设计稿宽度或者目前失常分辨率的1/10 selectorBlackList: [".ivu"],// 要疏忽的选择器并保留为px。 minPixelValue: 2,// 设置要替换的最小像素值。 "propList": [ "*" ]// 须要做转化解决的属性,如`hight`、`width`、`margin`等,`*`示意全副 } }} 4.找到 node_modules/lib-flexible/flexible.js 批改源文件(作者写死了宽度) function refreshRem(){ var width = docEl.getBoundingClientRect().width; if (width / dpr > 540) { //width = 540 * dpr; //获取屏幕宽度,如果宽度大于540,被写死了 width = width * dpr; //获取屏幕宽度 } var rem = width / 10; //缩放比例能够按理论来 docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; }我的屏幕是宽度1440,这样就能够了 ...

July 20, 2020 · 1 min · jiezi

vuecli-为项目设置别名

1.使用场景:在项目开发过程中经常需要引入各种文件,例img,css,js等,我们可以在vue-cli中给不同目录设置别名,方便我们使用 2.vue-cli 2x 配置 // 在 webpack.base.config.js中的 resolve 配置项,在其 alias 中增加别名resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }3.vue-cli 3x 配置 // 在根目录下创建vue.config.jsvar path = require('path')function resolve (dir) { console.log(__dirname) return path.join(__dirname, dir)}module.exports = { chainWebpack: config => { config.resolve.alias .set(key, value) // key,value自行定义,比如.set('@@', resolve('src/components')) }}4.保存,重启项目

October 15, 2019 · 1 min · jiezi

根据vuecli手摸手实现一个自己的脚手架

故事背景身为一个入门前端七个月的小菜鸡,在我入门前端的第一天就接触到了vue,并且死皮赖脸的跟他打了这么久的交到,还记得第一次用vue init webpack 这句命令一下生成一个模板的时候那种心情,当时我就想,要是自己也能写一个的话,那会是灰常吃鸡的吧 o( ̄▽ ̄)ブ,所以说今天我们也要简单实现一个自己的脚手架认识binbin的作用首先我们先来了解一下这个bin ,这个bin和我们最开始用的vue init webpack 这个命令是息息相关的 还记得我们在最开始安装vue-cli的时候嘛 npm install vue-cli -g 这条命令的意思是把vue-cli 安装到全局 ,以至于你再任何一个地方打开cmd 的时候都能够使用 vue init webpackvue init webpack 这条命令实际上是执行的vue-cli 里边 package.json 里边的bin属性下的命令 这个文件大概位置如下 这个路径里边有隐藏的路径,在查找的时候记得打开隐藏目录可见 这个bin里边大概长成这个样子 由图中可见,这里边有三个命令 vue vue-init vue-list这个三个命令的意思是执行对应的文件,Npm会在node_modules/.bin/目录下建立符号链接。又因为node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时,就可以不带路径,直接通过命令来调用这些文件。bin所执行的文件和参数那么说到这里你肯定会好奇这个文件是怎么做到生成模板的对吧,那么我们就来看一下被执行的这个文件到底是何方神圣 #!/usr/bin/env node const program = require('commander') program .version(require('../package').version) .usage('<command> [options]') .command('init', 'generate a new project from a template') .command('list', 'list available official templates') .command('build', 'prototype a new project') .command('create', '(for v3 warning only)') program.parse(process.argv)上边这一坨代码就是 执行vue init webpack 的文件所有内容 ...

October 4, 2019 · 2 min · jiezi

关于vuecli3跨域配置笔记

vue-cli3跨域配置:在vue-resource的数据请求中,一般我们会将请求方式GET/POST修改为jsonp的请求方式就可以实现跨域。但是对于只支持GET/POST两种请求方式的api,修改jsonp,就会出错。需要进行跨域的配置。(1)在文件根目录下,创建vue.config.js配置文件,具体配置如下:module.exports={ //将baseUrl:'/api',改为baseUrl:'/'baseUrl:'/',devServer:{ '/api':{ target:'http://apis.juhe.cn/', changeOrigin:true, ws:true, pathRewrite:{ '^/api':'' } }}}(2)在文件根目录下,创建.env.development 用作开发环境变量设置。在.env.development文件下设置变量 VUE_APP_BASE_API=/api 即可将devServer的proxy重写的url赋值给VUE_APP_BASE_API (3)读取配置的变量,通过process.env.VUE_APP_XX(process.env.VUE_APP_BASE_API)来读取。

August 21, 2019 · 1 min · jiezi

vuecli3配置与跨域处理

安装vue-cli3环境准备1.如果您已安装vue-cli2,请先删除当前脚手架,否则无法成功安装vue-cli3。 npm uninstall vue-cli -g2. 检查node.js版本。vue-cli3需要node版本大于8.9。在cmd中输入node -v查看版本。如果版本过低,请先去node官网中下载高版本。 安装脚手架vue-cli3的包名有vue-cli改为@vue/cli。 使用npm全局安装vue-cli3。 npm install @vue/cli -g然后使用vue -V 使用vue-cli3创建项目vue-cli3创建项目的方式和2.x也有一些区别。首先创建项目时给我们更多可配置的选项,在选择手动选择特性时,可以根据提示选择是否安装vue-router、vuex等。其次其目录结构也可2.x有区分,下文会详细讲。然后我们可以把本次安装配置保存为以后可以复用的preset,在以后创建项目时更快速。 1.输入vue create vue_cli3_test,上下键可以选择默认安装或者手动安装。这次我选择手动安装。 2.点击回车键,进入配置界面。将需要安装的配置前面勾选为*。空格键可以选中和取消选中。可以根据个人需求进行配置。安装成功后根据提示进入目录,并运行项目。 cd vue_cli3_testyarn serve 3.vue-cli3提供了图形界面方式进行创建项目, 可以在网页上直接进行项目的配置。vue ui 4.打开目录时发现配置文件都在了,大家不要慌张。可以在package.json文件的同级目录下创建vue.config.js文件。文件的格式应该为 // vue.config.jsmodule.exports = { // 选项...}下面为我的基础配置: module.exports = { outputDir: 'dist', //build输出目录 assetsDir: 'assets', //静态资源目录(js, css, img) lintOnSave: false, //是否开启eslint devServer: { open: true, //是否自动弹出浏览器页面 host: "localhost", port: '8081', https: false, //是否使用https协议 hotOnly: false, //是否开启热更新 proxy: null, }}官网给出了丰富而全面的配置,更多配置请参考https://cli.vuejs.org/zh/conf...。 ...

August 17, 2019 · 1 min · jiezi

vue-eslint简要配置

在前端开发过程中,难免习惯了console.log。 但是刚入门vue时,基于vue-cli 3.x,运行时终端老抛出error。一看信息,发现是不能使用console.log,另外import后的但是没有使用的变量也提示error信息,这是不错的。 1. 修改rules但的你想去掉console提示?那可以通过package.json修改rules规则。在package.json中,有这几其中的一项,在rules中添加"no-console": "off",如下: 修改完成后,重新运行即可生效。 "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/essential", "eslint:recommended" ], "rules": { "no-console": "off" }, "parserOptions": { "parser": "babel-eslint" } },2. eslintConfig说明"eslintConfig": { "root": true, // 此项是用来告诉eslint找当前配置文件不能往父级查找 "env": { "node": true // 此项指定环境的全局变量,下面的配置指定为node环境 }, "extends": [ // 此项是用来配置vue.js风格,就是说写代码的时候要规范的写,如果你使用vs-code我觉得应该可以避免出错 "plugin:vue/essential", "@vue/standard" ], "rules": { // 规则配置写在这里 "no-console": "off" }, "parserOptions": { "parser": "babel-eslint" // 此项是用来指定eslint解析器的,解析器必须符合规则,babel-eslint解析器是对babel解析器的包装使其与ESLint解析 } },2.1 关闭eslint(方案1)直接把package.json上面这一段删除 ...

July 26, 2019 · 2 min · jiezi

vuecli3-实现多页面应用

适用场景公司有专门的审批系统,我现在做的后台管理系统需要接入,移动端和PC端都要有一个页面来展示业务信息。后端不给开俩项目(也确实没必要),所以打算用多页面来解决,PC和移动端放到一个项目里,然后打包到各自的文件夹。 简单来说,多页面也就是适用于有多个小页面,不至于单独开多个项目的情况。 项目结构项目 src 文件夹结构如下: 打包之后 dist 文件夹结构如下: 修改哪些文件新增 utils 文件夹utils 文件夹下新增四个文件: getPages.js // 用来获取 pages 文件夹下的文件名称,vue.config.js 使用cssCopy.js // webpack 打包之后各页面的 css 文件复制到各个文件夹下jsCopy.js // webpack 打包之后各页面的 js 文件复制到各个文件夹下htmlReplace.js // 解决打包之后各页面 html 文件引入的 css、js 文件的路径问题vue.config.js为何添加 pages字段可参考 Vue 官方文档。 + let pageMethod = require('./utils/getPages.js')+ let pages = pageMethod.pages()module.exports = {- // publicPath: './', // 注意此行,会影响 htmlReplace.js 文件+ pages}注意事项每个页面的.html文件必须不能同名,不然本地开发环境无法拆分页面,本地如果想访问其他页面的话,地址如下:http://localhost:8080/order.html。 最好的处理方式是每个页面的文件夹名字和.vue名字和.html名字和.js的名字都一样,这样打包时容易拆分。 参考其实这篇文章不算原创,是参考其他文章写的,因为原文章真的是太简陋了.... 原文地址:CSDN - lizhen_software示例仓库地址:vue-more-pages

June 26, 2019 · 1 min · jiezi

vue多页面项目使用全局Loading组件

多页面vue应用中,在请求接口之前统一加上Loading作为未请求到数据之前的过渡展示。由于多页面未使用vuex做状态管理,只在入口统一注册了bus,所以此例子使用eventbus做事件通信。 在Loading.vue中,一个简单的公共loading组件这个loading组件用showLoading控制展示与否。 <template> <div class="loading" v-show="showLoading"> <img/> <p>加载中...</p> </div></template>Loading.vue中,用bus接收全局事件,控制组件的显示隐藏用SHOW_LOADING和HIDE_LOADING事件控制组件的显示隐藏 <script>import { Bus, SHOW_LOADING, HIDE_LOADING } from 'utils/bus'export default { name: 'loading', data () { return { showLoading: false } }, created () { this.loadingFn() }, methods: { loadingFn () { Bus.$on(SHOW_LOADING, () => { this.showLoading = true }) Bus.$on(HIDE_LOADING, () => { this.showLoading = false } },}</script>ajax请求中统一做处理以ajax请求为例,可以在beforeSend和complete钩子函数中emit对应的隐藏和显示事件。 new Promise((resolve, reject) => { let defaultOpt = { url, type: config.method || 'POST', data: params || {}, timeout: 50000, contentType: 'application/x-www-form-urlencoded', dataType: json } defaultOpt.beforeSend = (xhr, settings) => { if(config.setLoad){ Bus.$emit(SHOW_LOADING) } else { Bus.$emit(HIDE_LOADING) } } defaultOpt.complete = () => { Bus.$emit(HIDE_LOADING) } $.ajax(defaultOpt) })将bus注册在多页面的应用的main.js中由于每个页面都有可能用到这个效果,这时候会在全局注册一些公共的组件,这样哪个页面需要用到,不需要重新引入,直接调用组件即可。 ...

June 14, 2019 · 1 min · jiezi

To-Be-Simple基于elementUI的功能扩展组件系列1之Table篇

项目地址:tbs-ve-template 前言结合日常开发,封装常用功能,提高开发效率。让程序猿兄弟姐妹们也有时间约约女票,逗逗男票,做做自己想做的事情,不要天天在办公室造轮子! 1.通用Table思路类似easy-ui的table加载方式 环境简述开发框架:基于vue-admin-templategithub:https://github.com/PanJiaChen... JS 包管理工具: Yarn安装方法:https://www.cnblogs.com/xiang... 项目 启动: 第一步:yarn install 下载所有依赖包第二步:yarn run dev 下载所有依赖包第三步:访问http://localhost:9528 项目结构说明:为了避免代码过长不易浏览,讲vue代码与js代码分开编辑。 1. 通用Table显示效果 支持类型类型:文本 | 链接 | 文档 | 图片 用法 相关属性Table 属性: 参数说明类型可选值默认值listLoading动画效果boolean—truecolumnstable 的列(column)的配置对象,更多细节请参见列(column)属性。array——uitable显示效果的配置对象,更多细节请参见列(ui)属性array——data显示数据集合,一般从远程获取数据后进行赋值array——pagetable分页配置对象,更多细节请参见列(page)属性array——Table列(Column)属性: 参数说明类型可选值默认值label列的标题文本string——field列的字段名,与data属性中的名称对应string——width列的宽度number——showtype列的类型,normal:为文本类型stringnormal、http、file、imagenormalfilter过滤器,类似easyui中formatter属性,用于格式数据string——Table样式(ui)属性: 参数说明类型可选值默认值tableHeightTable高string、number——tableWidthTable宽度string、number——Table分页(page)属性: 参数说明类型可选值默认值total总条目数number——listQuery分页参数,listQuery:{ page:当前页码,limit:每页条目数 }number——有问题反馈在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流 QQ: 375766253邮件:375766253@qq.com

June 10, 2019 · 1 min · jiezi

vuecli3项目展示本地Markdown文件

【版本】vue-cli3webpack@4.33.0【步骤】1、安装插件vue-markdown-loader npm i vue-markdown-loader -Dps:这个插件是基于markdown-it的,不需要单独安装markdown-it。 2、修改vue.config.js配置文件(如果没有,在项目根目录新建一个): module.exports = { chainWebpack: config => { config.module.rule('md') .test(/\.md/) .use('vue-loader') .loader('vue-loader') .end() .use('vue-markdown-loader') .loader('vue-markdown-loader/lib/markdown-compiler') .options({ raw: true }) }}3、直接上代码 App.vue <template> <my-markdown></my-markdown></template><script>import myMarkdown from './assets/cpu.md';export default { components: { myMarkdown },</script>正常情况下,到这里就结束了。 【坑】由于业务给我的Markdown文档的标题是这样的: ##物理CPU个数物理CPU数就是主板上实际插入的CPU数量……得到的结果并不让我满意: 折腾了大半天才发现,这是由于标题的#井号和文字之间没有空格导致的。证明见下: var MarkdownIt = require('markdown-it'),md = new MarkdownIt();console.log(md.render('# markdown-it rulezz!'))//<h1>markdown-it rulezz!</h1>console.log(md.render('#markdown-it rulezz!'))//<p>#markdown-it rulezz!</p>Fine :) 愚蠢的我想出了一个解决办法:因为HyperDown.js能避免上面那样的情况,于是我用它来对文档做预处理。 安装HyperDown.js npm install hyperdown -D然后把vue.config.js改成了这样。 let HyperDown = require('hyperdown');let parser = new HyperDown;module.exports = { chainWebpack: config => { config.module.rule('md') .test(/\.md/) .use('vue-loader') .loader('vue-loader') .end() .use('vue-markdown-loader') .loader('vue-markdown-loader/lib/markdown-compiler') .options({ // markdown-it config preset: 'default', breaks: true, raw: true, typographer: true, preprocess: function(markdownIt, source) { return parser.makeHtml(source);//重点在这里!!! } }) }}END ...

June 6, 2019 · 1 min · jiezi

10分钟搭建属于自己的 Vue CLI3 项目模板

0x01 契机Vue CLI3 出来已经很长时间了,一直想研究它的插件系统却没有时间(其实是懒),刚好最近需要统一一下项目组的规范(借口),于是就有了契机。 先瞅一眼文档: CLI3插件和Preset 然后就教你怎么完全定制化一套 前端项目模板,妈妈再也不用担心我每次复制粘贴啦~ 特别说明:这种 preset 不需要发布到 npm,支持 github,gitlab 及任何 git repo,甚至可以直接本地引入哦~ 0x02 两个名词插件顾名思义,就是插件啦 Vue CLI 使用了一套基于插件的架构基于插件的架构使得 Vue CLI 灵活且可扩展Preset可以翻译为 预设 一个包含创建新项目所需预定义选项和插件的 JSON 对象还可以理解为一套预置的项目模板,也就是本文要讲的。使用vue create 创建过项目的小伙伴应该都记得,在创建完成后 CLI 会提示是否保存为一个 preset,这里第一条指的就是要保存的那个对象。如果你保存过,下面的命令就能看到之前保存的 preset。 cat ~/.vuerc每个 preset.json 大概是这么个格式: { "useConfigFiles": true, "plugins": {...}, "configs": { "vue": {...}, "postcss": {...}, "eslintConfig": {...}, "jest": {...} }}0x03 两者区别插件一个插件包含以下三个部分: Service 插件generator 文件 (可选)prompts 文件 (可选)Preset一个 Preset 项目包含以下三个部分 preset.jsongenerator 文件 (可选)prompts 文件 (可选)可以看到他们两个的区别就是插件必须有一个 Service 插件(这个东西比本文讲的插件范畴要窄),而 Preset 必须包含一个 preset.json ...

April 21, 2019 · 2 min · jiezi

vue cli3

常见问题关于tunneling socket could not be established , cause=getaddrinfo ENOTFOUND 错误的解决方法初始化一个vue项目的时候,出现了一些问题因为我的代理设置的是这个:

April 13, 2019 · 1 min · jiezi

vue-cli3.0源码分析@vue/cli-----add和invoke

上一篇已经讲了create命令;那么这一篇我们来看一下add和invoke这个命令。之所以放一起讲,是因为当add执行的时候,也会去执行invokeaddvue add vue-cli-plugin-xxx 或 vue add @vue/xxx通过这种形式就是vue-cli3.0内部能识别的插件了首先来看一下入口program .command(‘add <plugin> [pluginOptions]’) .description(‘install a plugin and invoke its generator in an already created project’) .option(’–registry <url>’, ‘Use specified npm registry when installing dependencies (only for npm)’) // 可以设置源 .allowUnknownOption() .action((plugin) => { require(’../lib/add’)(plugin, minimist(process.argv.slice(3))) })入口比较简单,接下来我们来看一下add.js文件async function add (pluginName, options = {}, context = process.cwd()) { // special internal “plugins” // 这边对@vue/router和@vue/vuex这2个插件做特殊处理,直接从cli-service下拉模块 if (/^(@vue/)?router$/.test(pluginName)) { return addRouter(context) } if (/^(@vue/)?vuex$/.test(pluginName)) { return addVuex(context) } const packageName = resolvePluginId(pluginName) // 解析插件名 log() log(???? Installing ${chalk.cyan(packageName)}...) log() const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? ‘yarn’ : ’npm’) // 是用什么安装 npm、yarn await installPackage(context, packageManager, options.registry, packageName) // 开始安装插件 log(${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}) log() const generatorPath = resolveModule(${packageName}/generator, context) // 解析路径 // 开始加载插件下面的generator if (generatorPath) { invoke(pluginName, options, context) } else { log(Plugin ${packageName} does not have a generator to invoke) }}这边也比较简单一目了然。async function addRouter (context) { const inquirer = require(‘inquirer’) const options = await inquirer.prompt([{ name: ‘routerHistoryMode’, type: ‘confirm’, message: Use history mode for router? ${chalk.yellow((Requires proper server setup for index fallback in production))} }]) invoke.runGenerator(context, { id: ‘core:router’, apply: loadModule(’@vue/cli-service/generator/router’, context), options })}async function addVuex (context) { invoke.runGenerator(context, { id: ‘core:vuex’, apply: loadModule(’@vue/cli-service/generator/vuex’, context) })}这2个就是单独添加router和vuexexports.resolvePluginId = id => { // already full id // e.g. vue-cli-plugin-foo, @vue/cli-plugin-foo, @bar/vue-cli-plugin-foo if (pluginRE.test(id)) { return id } // scoped short // e.g. @vue/foo, @bar/foo if (id.charAt(0) === ‘@’) { const scopeMatch = id.match(scopeRE) if (scopeMatch) { const scope = scopeMatch[0] const shortId = id.replace(scopeRE, ‘’) return ${scope}${scope === '@vue/' ? `` : vue-}cli-plugin-${shortId} } } // default short // e.g. foo return vue-cli-plugin-${id}}将@vue/xxx的形状解析为vue-cli-plugin-xxx这边的主要流程就是安装插件并注入invokeinvoke同样我们先来看一看入口program .command(‘invoke <plugin> [pluginOptions]’) .description(‘invoke the generator of a plugin in an already created project’) .option(’–registry <url>’, ‘Use specified npm registry when installing dependencies (only for npm)’) .allowUnknownOption() .action((plugin) => { require(’../lib/invoke’)(plugin, minimist(process.argv.slice(3))) })在add中的代码与入口调用是一样的,都是通过调用invoke.jsinvoke(pluginName, options, context)那么就来看看invoke.js内部是怎么实现的,主要就是分为以下几步信息验证加载插件信息generator/prompts运行Generator信息验证:const pkg = getPkg(context) // package文件 // attempt to locate the plugin in package.json const findPlugin = deps => { if (!deps) return let name // official if (deps[(name = @vue/cli-plugin-${pluginName})]) { return name } // full id, scoped short, or default short if (deps[(name = resolvePluginId(pluginName))]) { return name } } const id = findPlugin(pkg.devDependencies) || findPlugin(pkg.dependencies) // 在devDependencies和dependencies依赖中寻找vue-cli插件 if (!id) { throw new Error( Cannot resolve plugin ${chalk.yellow(pluginName)} from package.json. + Did you forget to install it? ) }以上验证是否存在package.json文件,以及package文件内是否安装了vue-cli插件加载插件const pluginGenerator = loadModule(${id}/generator, context) // 加载插件下的generator文件 if (!pluginGenerator) { throw new Error(Plugin ${id} does not have a generator.) } // resolve options if no command line options (other than –registry) are passed, // and the plugin contains a prompt module. // eslint-disable-next-line prefer-const let { registry, …pluginOptions } = options if (!Object.keys(pluginOptions).length) { let pluginPrompts = loadModule(${id}/prompts, context) // 加载插件下的prompts,对话 if (pluginPrompts) { if (typeof pluginPrompts === ‘function’) { pluginPrompts = pluginPrompts(pkg) } if (typeof pluginPrompts.getPrompts === ‘function’) { pluginPrompts = pluginPrompts.getPrompts(pkg) } pluginOptions = await inquirer.prompt(pluginPrompts) } }以上就是加载了generator和prompts,用来运行插件的一些内置代码Generatorconst generator = new Generator(context, { pkg, plugins: [plugin], files: await readFiles(context), completeCbs: createCompleteCbs, invoking: true })这边的跟create中一样效果最后router和vuex是直接到Generator步骤,前面的加载省略了。 ...

April 13, 2019 · 3 min · jiezi

vue-cli3 开启HTTPS

项目里边准备试试html5的录音。然后调研一波,发现大坑:必须使用HTTPS才能录音。然后就想着本地模拟HTTPS录音。项目用的vue-cli3,折腾一波,发现开启非常简单:创建配置文件 项目根目录新建 vue.config.js 文件。粘贴代码module.exports = { devServer: { https: true }}不过,好像模拟的假的HTTPS,调用api还是会报错。然后太过麻烦,放弃这个录音feature。

April 10, 2019 · 1 min · jiezi

vue-cli3.0源码分析@vue/cli-----create

本文主要学习vue-cli3.0的源码的记录。源码地址: https://github.com/vuejs/vue-cli主要对packages里面的@vue进行学习。如下图 在图中我们可以看到vue-cli中,不仅仅有初始化工程,还有许多通用的工具、插件。接下来我们就对这些插件进行学习。首先我们来看cli的目录:首先来看package.json{ “name”: “@vue/cli”, // 名称 “version”: “3.5.5”, // 版本号 “bin”: { “vue”: “bin/vue.js” }, // 这个是用于命令窗口执行的命令;如果是全局安装了,那么vue就是一个命令值 vue xxxx"engines": { “node”: “>=8.9” } // 需要的node版本号}我们现在我们可以去看bin/vue.js文件,对该文件给出注释,方便阅读#!/usr/bin/env node// 这边备注是node来解析, 当然如果不写也没事// Check node version before requiring/doing anything else// The user may be on a very old node versionconst chalk = require(‘chalk’) // 用于输出有色彩const semver = require(‘semver’) // 用于比较版本号const requiredVersion = require(’../package.json’).engines.node // 获取node版本号要求// 检测node的版本号,如果不符合要求就给提示function checkNodeVersion (wanted, id) { if (!semver.satisfies(process.version, wanted)) { // process.version表示当前node版本 console.log(chalk.red( ‘You are using Node ’ + process.version + ‘, but this version of ’ + id + ’ requires Node ’ + wanted + ‘.\nPlease upgrade your Node version.’ )) // 给出当前vue-cli需要的版本为多少 process.exit(1) }}checkNodeVersion(requiredVersion, ‘vue-cli’)if (semver.satisfies(process.version, ‘9.x’)) { console.log(chalk.red( You are using Node ${process.version}.\n + Node.js 9.x has already reached end-of-life and will not be supported in future major releases.\n + It's strongly recommended to use an active LTS version instead. ))} // 如果node为9.x那么给出相应的提示const fs = require(‘fs’) // 文件const path = require(‘path’) // 路径const slash = require(‘slash’) // 用于转换 Windows 反斜杠路径转换为正斜杠路径 \ => /const minimist = require(‘minimist’) // 用来解析从参数// enter debug mode when creating test repoif ( slash(process.cwd()).indexOf(’/packages/test’) > 0 && ( // process.cwd()为当前绝对路径,如F:\packages@vue\cli\bin fs.existsSync(path.resolve(process.cwd(), ‘../@vue’)) || fs.existsSync(path.resolve(process.cwd(), ‘../../@vue’)) )) { process.env.VUE_CLI_DEBUG = true}const program = require(‘commander’) // node对话,输入const loadCommand = require(’../lib/util/loadCommand’) // 用于查找模块program .version(require(’../package’).version) .usage(’<command> [options]’)上述是一些检测的代码。之后就要开始交互式的命令了。1.create入口我们可以看到program.command就是创建的一个命令,后面会有很多的命令create,add,invoke等等,这一节主要来讲解createprogram .command(‘create <app-name>’) .description(‘create a new project powered by vue-cli-service’) .option(’-p, –preset <presetName>’, ‘Skip prompts and use saved or remote preset’) .option(’-d, –default’, ‘Skip prompts and use default preset’) .option(’-i, –inlinePreset <json>’, ‘Skip prompts and use inline JSON string as preset’) .option(’-m, –packageManager <command>’, ‘Use specified npm client when installing dependencies’) .option(’-r, –registry <url>’, ‘Use specified npm registry when installing dependencies (only for npm)’) .option(’-g, –git [message]’, ‘Force git initialization with initial commit message’) .option(’-n, –no-git’, ‘Skip git initialization’) .option(’-f, –force’, ‘Overwrite target directory if it exists’) .option(’-c, –clone’, ‘Use git clone when fetching remote preset’) .option(’-x, –proxy’, ‘Use specified proxy when creating project’) .option(’-b, –bare’, ‘Scaffold project without beginner instructions’) .option(’–skipGetStarted’, ‘Skip displaying “Get started” instructions’) .action((name, cmd) => { const options = cleanArgs(cmd) if (minimist(process.argv.slice(3))..length > 1) { console.log(chalk.yellow(’\n Info: You provided more than one argument. The first one will be used as the app's name, the rest are ignored.’)) } // –git makes commander to default git to true if (process.argv.includes(’-g’) || process.argv.includes(’–git’)) { options.forceGit = true } require(’../lib/create’)(name, options) })这边创建了一个command(‘create <app-name>’),如果是全局安装了@vue-cli3.0,那么就可以使用vue create xxxx yyyxxx yyy为文件名称和option这些参数配置项。我们来看一下分别有哪些配置项:-p, –preset <presetName> 忽略提示符并使用已保存的或远程的预设选项-d, –default 忽略提示符并使用默认预设选项-i, –inlinePreset <json> 忽略提示符并使用内联的 JSON 字符串预设选项-m, –packageManager <command> 在安装依赖时使用指定的 npm 客户端-r, –registry <url> 在安装依赖时使用指定的 npm registry-g, –git [message] 强制 / 跳过 git 初始化,并可选的指定初始化提交信息-n, –no-git 跳过 git 初始化-f, –force 覆写目标目录可能存在的配置-c, –clone 使用 git clone 获取远程预设选项-x, –proxy 使用指定的代理创建项目-b, –bare 创建项目时省略默认组件中的新手指导信息-h, –help 输出使用帮助信息在action中就是进入命令后的执行代码,以下部分主要就是提取-g命令,const options = cleanArgs(cmd) if (minimist(process.argv.slice(3))..length > 1) { console.log(chalk.yellow(’\n Info: You provided more than one argument. The first one will be used as the app's name, the rest are ignored.’)) } // –git makes commander to default git to true if (process.argv.includes(’-g’) || process.argv.includes(’–git’)) { options.forceGit = true }2.基础信息验证接下来就是进入create.js文件async function create (projectName, options) { // 代理使用 -x 或–proxy参数配置 if (options.proxy) { process.env.HTTP_PROXY = options.proxy } const cwd = options.cwd || process.cwd() // 当前目录 const inCurrent = projectName === ‘.’ // 是否存在当前目录 const name = inCurrent ? path.relative(’../’, cwd) : projectName // 项目名称 const targetDir = path.resolve(cwd, projectName || ‘.’) // 生成项目目录 const result = validateProjectName(name) // 验证名称是否符合规范 if (!result.validForNewPackages) { console.error(chalk.red(Invalid project name: "${name}")) result.errors && result.errors.forEach(err => { console.error(chalk.red.dim(‘Error: ’ + err)) }) result.warnings && result.warnings.forEach(warn => { console.error(chalk.red.dim(‘Warning: ’ + warn)) }) exit(1) } // 检测文件是否存在, if (fs.existsSync(targetDir)) { if (options.force) { await fs.remove(targetDir) // 这边强制覆盖 } else { await clearConsole() if (inCurrent) { // 这边提示是否在当前文件创建? const { ok } = await inquirer.prompt([ { name: ‘ok’, type: ‘confirm’, message: Generate project in current directory? } ]) if (!ok) { return } } else { // 文件已重复 const { action } = await inquirer.prompt([ { name: ‘action’, type: ’list’, message: Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:, choices: [ { name: ‘Overwrite’, value: ‘overwrite’ }, { name: ‘Merge’, value: ‘merge’ }, { name: ‘Cancel’, value: false } ] } ]) if (!action) { return } else if (action === ‘overwrite’) { console.log(\nRemoving ${chalk.cyan(targetDir)}...) await fs.remove(targetDir) } } } } // 新建构造器 const creator = new Creator(name, targetDir, getPromptModules()) // getPromptModules()为内置插件对话对象 await creator.create(options)}以上大部分都是定义文件,目录和一名称效验,文件效验,比较简单易懂,接下来就是创建Creator构造器了3.Creator构造器这一节的内容会比较绕一点。首先我们先来了解一下vue-cli-preset,这是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。 vue create 过程中保存的 preset 会被放在你的用户目录下的一个配置文件中 (~/.vuerc)。你可以通过直接编辑这个文件来调整、添加、删除保存好的 preset。这里有一个 preset 的示例:{ “useConfigFiles”: true, “router”: true, “vuex”: true, “cssPreprocessor”: “sass”, “plugins”: { “@vue/cli-plugin-babel”: {}, “@vue/cli-plugin-eslint”: { “config”: “airbnb”, “lintOn”: [“save”, “commit”] } }}Preset 的数据会被插件生成器用来生成相应的项目文件。除了上述这些字段,你也可以为集成工具添加配置:{ “useConfigFiles”: true, “plugins”: {…}, “configs”: { “vue”: {…}, “postcss”: {…}, “eslintConfig”: {…}, “jest”: {…} }}这些额外的配置将会根据 useConfigFiles 的值被合并到 package.json 或相应的配置文件中。例如,当 “useConfigFiles”: true 的时候,configs 的值将会被合并到 vue.config.js 中。更多关于 preset 可以前往 vue-cli 官网 插件和 Preset https://cli.vuejs.org/zh/guid…。在基础验证完后会创建一个Creator实例const creator = new Creator(name, targetDir, getPromptModules())3.1getPromptModules在分析Creator之前,我们先来看一下getPromptModules是什么。getPromptModules源码exports.getPromptModules = () => { return [ ‘babel’, ’typescript’, ‘pwa’, ‘router’, ‘vuex’, ‘cssPreprocessors’, ’linter’, ‘unit’, ’e2e’ ].map(file => require(../promptModules/${file}))}我们可以在promptModules中分别看到其中比如unit.js:module.exports = cli => { cli.injectFeature({ name: ‘Unit Testing’, value: ‘unit’, short: ‘Unit’, description: ‘Add a Unit Testing solution like Jest or Mocha’, link: ‘https://cli.vuejs.org/config/#unit-testing', plugins: [‘unit-jest’, ‘unit-mocha’] }) cli.injectPrompt({ name: ‘unit’, when: answers => answers.features.includes(‘unit’), type: ’list’, message: ‘Pick a unit testing solution:’, choices: [ { name: ‘Mocha + Chai’, value: ‘mocha’, short: ‘Mocha’ }, { name: ‘Jest’, value: ‘jest’, short: ‘Jest’ } ] }) cli.onPromptComplete((answers, options) => { if (answers.unit === ‘mocha’) { options.plugins[’@vue/cli-plugin-unit-mocha’] = {} } else if (answers.unit === ‘jest’) { options.plugins[’@vue/cli-plugin-unit-jest’] = {} } })}我们可以看到这部其实就是对一些内置插件的一些配置项,用于对话后来进行安装,cli.injectFeature:是用来注入featurePrompt,即初始化项目时,选择的babel、typescript等cli.injectPrompt:是根据选择的 featurePrompt 然后注入对应的 prompt,当选择了 unit,接下来会有以下的 prompt,选择 Mocha + Chai 还是 Jestcli.onPromptComplete: 就是一个回调,会根据选择来添加对应的插件, 当选择了 mocha ,那么就会添加 @vue/cli-plugin-unit-mocha 插件3.2 new Creator接下来我们来看一下其构造函数constructor (name, context, promptModules) { super() this.name = name // 目录名称 this.context = process.env.VUE_CLI_CONTEXT = context // 当前目录 const { presetPrompt, featurePrompt } = this.resolveIntroPrompts() // 之前预制的插件,和项目的一些feature this.presetPrompt = presetPrompt this.featurePrompt = featurePrompt this.outroPrompts = this.resolveOutroPrompts() // 其他的插件 this.injectedPrompts = [] // 当选择featurePrompt时,注入的prompts this.promptCompleteCbs = [] this.createCompleteCbs = [] this.run = this.run.bind(this) const promptAPI = new PromptModuleAPI(this) promptModules.forEach(m => m(promptAPI)) }上述代码我们主要来看一下PromptModuleAPI,其他都是一些变量初始化的定义module.exports = class PromptModuleAPI { constructor (creator) { this.creator = creator } injectFeature (feature) { this.creator.featurePrompt.choices.push(feature) } injectPrompt (prompt) { this.creator.injectedPrompts.push(prompt) } injectOptionForPrompt (name, option) { this.creator.injectedPrompts.find(f => { return f.name === name }).choices.push(option) } onPromptComplete (cb) { this.creator.promptCompleteCbs.push(cb) }}这边是创建一个PromptModuleAPI实例,并通过promptModules.forEach(m => m(promptAPI)),将预设的内置插件加入到this.creator.featurePrompt,this.creator.injectedPrompts和this.creator.promptCompleteCbs中3.3getPreset在创建了Creator实例后,然后调用了create方法await creator.create(options)create方法源码,这段代码比较简单,主要是判断是否有-p,-d,-i的配置项来直接安装,如果没有的话,就进入对话模式this.promptAndResolvePreset,来选择性的安装const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG const { run, name, context, createCompleteCbs } = this if (!preset) { // 是否存在 -p 或–preset if (cliOptions.preset) { // vue create foo –preset bar preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone) } else if (cliOptions.default) { // 是否有-d或–default的命令,如果有则,默认直接安装 // vue create foo –default preset = defaults.presets.default } else if (cliOptions.inlinePreset) { // 是否有–inlinePreset或-i来注入插件 // vue create foo –inlinePreset {…} try { preset = JSON.parse(cliOptions.inlinePreset) } catch (e) { error(CLI inline preset is not valid JSON: ${cliOptions.inlinePreset}) exit(1) } } else { preset = await this.promptAndResolvePreset() } }先判断 vue create 命令是否带有 -p 选项,如果有的话会调用 resolvePreset 去解析 preset。resolvePreset 函数会先获取 ~/.vuerc 中保存的 preset, 然后进行遍历,如果里面包含了 -p 中的 <presetName>,则返回~/.vuerc 中的 preset。如果没有则判断是否是采用内联的 JSON 字符串预设选项,如果是就会解析 .json 文件,并返回 preset,还有一种情况就是从远程获取 preset(利用 download-git-repo 下载远程的 preset.json)并返回。上面的情况是当 vue create 命令带有 -p 选项的时候才会执行,如果没有就会调用 promptAndResolvePreset 函数利用 inquirer.prompt 以命令后交互的形式来获取 preset,下面看下 promptAndResolvePreset 函数的源码:async promptAndResolvePreset (answers = null) { // prompt if (!answers) { await clearConsole(true) answers = await inquirer.prompt(this.resolveFinalPrompts()) // 交互式命令对话,安装defalut和Manually select features } debug(‘vue-cli:answers’)(answers) if (answers.packageManager) { saveOptions({ packageManager: answers.packageManager }) } let preset if (answers.preset && answers.preset !== ‘manual’) { // 如果是选择本地保存的preset(.vuerc) preset = await this.resolvePreset(answers.preset) } else { // manual preset = { useConfigFiles: answers.useConfigFiles === ‘files’, plugins: {} } answers.features = answers.features || [] // run cb registered by prompt modules to finalize the preset this.promptCompleteCbs.forEach(cb => cb(answers, preset)) } // validate validatePreset(preset) // save preset if (answers.save && answers.saveName) { savePreset(answers.saveName, preset) } debug(‘vue-cli:preset’)(preset) return preset }看到这里会比较乱的,preset会比较多,我们再来看一下resolveFinalPrompts源码 resolveFinalPrompts () { // patch generator-injected prompts to only show in manual mode this.injectedPrompts.forEach(prompt => { const originalWhen = prompt.when || (() => true) prompt.when = answers => { return isManualMode(answers) && originalWhen(answers) } }) const prompts = [ this.presetPrompt, this.featurePrompt, …this.injectedPrompts, …this.outroPrompts ] debug(‘vue-cli:prompts’)(prompts) console.log(1, prompts) return prompts }这里我们可以看到presetPrompt, featurePrompt, injectedPrompts, outroPrompts 合并成一个数组进行返回,presetPrompt是预设的,当上一次选择manually模式进行了预设,并保存到.vuerc中,那么初始化的时候会列出已保存的插件featurePrompt就是内置的一些插件injectedPrompts是通过-i命令来手动注入的插件outroPrompts是一些其他的插件。这边对话完之后,就要开始依赖的安装了。4依赖安装我们继把create中的代码往下走const packageManager = ( cliOptions.packageManager || loadOptions().packageManager || (hasYarn() ? ‘yarn’ : ’npm’) ) await clearConsole() // 清空控制台 logWithSpinner(✨, Creating project in ${chalk.yellow(context)}.) this.emit(‘creation’, { event: ‘creating’ }) // get latest CLI version const { latest } = await getVersions() const latestMinor = ${semver.major(latest)}.${semver.minor(latest)}.0 // generate package.json with plugin dependencies const pkg = { name, version: ‘0.1.0’, private: true, devDependencies: {} } const deps = Object.keys(preset.plugins) deps.forEach(dep => { if (preset.plugins[dep]._isPreset) { return } // Note: the default creator includes no more than @vue/cli-* & @vue/babel-preset-env, // so it is fine to only test @vue prefix. // Other @vue/* packages’ version may not be in sync with the cli itself. pkg.devDependencies[dep] = ( preset.plugins[dep].version || ((/^@vue/.test(dep)) ? ^${latestMinor} : latest) ) }) // write package.json await writeFileTree(context, { ‘package.json’: JSON.stringify(pkg, null, 2) })这边主要就是获取cli的版本和生产package.json,其中主要是获取版本号module.exports = async function getVersions () { if (sessionCached) { return sessionCached } let latest const local = require(../../package.json).version if (process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG) { return (sessionCached = { current: local, latest: local }) } const { latestVersion = local, lastChecked = 0 } = loadOptions() const cached = latestVersion const daysPassed = (Date.now() - lastChecked) / (60 * 60 * 1000 * 24) if (daysPassed > 1) { // 距离上次检查更新超过一天 // if we haven’t check for a new version in a day, wait for the check // before proceeding latest = await getAndCacheLatestVersion(cached) } else { // Otherwise, do a check in the background. If the result was updated, // it will be used for the next 24 hours. getAndCacheLatestVersion(cached) // 后台更新 latest = cached } return (sessionCached = { current: local, latest })}// fetch the latest version and save it on disk// so that it is available immediately next timeasync function getAndCacheLatestVersion (cached) { const getPackageVersion = require(’./getPackageVersion’) const res = await getPackageVersion(‘vue-cli-version-marker’, ’latest’) if (res.statusCode === 200) { const { version } = res.body if (semver.valid(version) && version !== cached) { saveOptions({ latestVersion: version, lastChecked: Date.now() }) return version } } return cached}这边主要是有2个版本变量,一个是local本地cli版本。另一个laset远程cli版本另外getAndCacheLatestVersion而是通过 vue-cli-version-marker npm 包获取的 CLI 版本。生产package.json之后,我们在继续看后面代码const shouldInitGit = this.shouldInitGit(cliOptions) if (shouldInitGit) { logWithSpinner(????, Initializing git repository...) this.emit(‘creation’, { event: ‘git-init’ }) await run(‘git init’) } // install plugins stopSpinner() log(⚙ Installing CLI plugins. This might take a while...) log() this.emit(‘creation’, { event: ‘plugins-install’ }) if (isTestOrDebug) { // in development, avoid installation process await require(’./util/setupDevProject’)(context) } else { await installDeps(context, packageManager, cliOptions.registry) }这段代码首先会调用shouldInitGit来判断是否需要git初始化,判断的情景是:是否安装了git;命令中是否有-g或–git,或–no-git或-n;生成的目录是否包含了git判断完之后需要git初始化项目后,接下来就会调用installDeps来安装依赖exports.installDeps = async function installDeps (targetDir, command, cliRegistry) { const args = [] if (command === ’npm’) { args.push(‘install’, ‘–loglevel’, ’error’) } else if (command === ‘yarn’) { // do nothing } else { throw new Error(Unknown package manager: ${command}) } await addRegistryToArgs(command, args, cliRegistry) debug(command: , command) // DEBUG=vue-cli:install vue create demo debug(args: , args) await executeCommand(command, args, targetDir)} ...

April 10, 2019 · 9 min · jiezi

vueSSR: 从0到1构建vueSSR项目 --- vuex的配置(数据预取)

vuex的相关配置上一章做了node以及vue-cli3的配置,今天就来做vuex的部分。先打开官方文档-数据预取和状态看完之后,发现大致的逻辑就是利用mixin,拦截页面渲染完成之前,查看当前实例是否含有’asyncData’函数(由你创建以及任意名称),如果含有就进行调用,并且传入你需要的对象比如(store,route)例子// pages/vuex/index.jsexport default { name: “vuex”, asyncData({ store, route }) { // 触发 action 后,会返回 Promise return store.dispatch(‘fetchItem’, route.path) }, computed: { // 从 store 的 state 对象中的获取 item。 item() { return this.$store.state.items[this.$route.path] } }}//mixinimport Vue from ‘vue’;Vue.mixin({ beforeMount() { const { asyncData } = this.$options if (asyncData) { this.dataPromise = asyncData({ store: this.$store, route: this.$route }) } }})上面只是个大概的示例,下面开始正式来做吧。先创建一些文件 src/store.config.js 跟router.config.js 一样,在服务端运行避免状态单例 src/pages/store/all.js 全局公共模块// src/pages/store/all.js const all = { //开启命名空间 namespaced: true, //ssr注意事项 state 必须为函数 state: () => ({ count:0 }), mutations: { inc: state => state.count++ }, actions: { inc: ({ commit }) => commit(‘inc’) } } export default all;vuex 模块all 单独一个全局模块如果home有自己的数据,那么就在home下 惰性注册模块但是记得页面销毁时,也一定要销毁模块!!!因为当多次访问路由时,可以避免在客户端重复注册模块。如果想只有个别几个路由共用一个模块,可以在all里面进行模块嵌套,或者将这个几个页面归纳到一个父级路由下 ,在父级实例进行模块惰性注册。// home/index.jsimport all from ‘../store/all.js’;export default { name: ‘home’, computed:{ count(){ return this.$store.state.all.count } }, asyncData({ store}){ store.registerModule(‘all’,all); return store.dispatch(‘all/inc’) }, data() { return { activeIndex2: ‘1’, show:false, nav:[ { path:’/home/vue’, name:‘vue’ }, { path:’/home/vuex’, name:‘vue-vuex’ }, { path:’/home/vueCli3’, name:‘vue-cli3’ }, { path:’/home/vueSSR’, name:‘vue ssr’ } ] }; }, watch:{ $route:function(){ if(this.show){ this.show = false; } //切换路由时,进行自增 this.$store.dispatch(‘all/inc’); } }, mounted() { //做额外请求时,在mounted下进行 }, methods: { user_info(){ this.http.post(’/cms/i1/user_info’).then(res=> { console.log(res.data); }).catch( error => { console.log(error) }) } }, destroyed(){ this.$store.unregisterModule(‘all’) }}数据预取// store.config.js //store总配置 import Vue from ‘vue’; import Vuex from ‘vuex’; Vue.use(Vuex); //预请求数据 function fetchApi(id){ //该函数是运行在node环境 所以需要加上域名 return axios.post(‘http://localhost:8080/cms/i1/user_info’); } //返回 Vuex实例 避免在服务端运行时的单利状态 export function createStore() { return new Vuex.Store({ state:{ items:{} }, actions: { fetchItem ({commit}, id) { return fetchApi(id).then(item => { commit(‘setItem’,{id, item}) }) } }, mutations: { setItem(state, {id, item}){ Vue.set(state.items, id, item.data) } } }) }mixin相关在src下新建个methods 文件夹,这里存放写vue的全局代码以及配置 获取当前实例// src/methods/index.jsimport ‘./mixin’;import Vue from ‘vue’;import axios from ‘axios’;Vue.prototype.http = axios;// src/methods/mixin/index.js import Vue from ‘vue’; Vue.mixin({ beforeMount() { const { asyncData } = this.$options;//这里 自己打印下就知道了。就不过多解释了 //当前实例是否有该函数 if (asyncData) { // 有就执行,并传入相应的参数。 asyncData({ store: this.$store, route: this.$route }) } } })main.js 新增代码 import Vue from ‘vue’; Vue.config.productionTip = false; import VueRouter from ‘vue-router’; import App from ‘./App.vue’;+ import ‘./methods’; //同步路由状态+ import { sync } from ‘vuex-router-sync’; import { createRouter } from ‘./router.config.js’;+ import { createStore } from ‘./store.config.js’; export function createApp() { const router = createRouter() const store = createStore() //同步路由状态(route state)到 store sync(store, router) const app = new Vue({ router,+ store, render: h => h(App) }) return { app, router,+ store }; }下面更新,开发环境部署相关 ...

March 6, 2019 · 2 min · jiezi

vue-cli3+typescript初体验——项目实战

前言上周开始做vue+ts的前置准备工作,花了一周时间熟悉vue3基于类——class的写法,主要是vuex、vue-router、props、components、emit、watch等这些东东的使用规则。目前已经算是摸底结束,开始进行项目实战了,正好有一个新的项目交到我的手上,没跑了,就是vue+ts。项目介绍这是一个移动端商城项目,产品原型还没出来,技术先行,所以可以随意发挥,UI方面暂时不做考虑。主要的功能就是商品展示、商品详情、商品购买、购物车、订单列表、订单详情、个人中心、收货地址等模块。技术介绍vue、vuex、vue-router、vue-cli3、typescript、axios、vue-awesome-swiper等。因为之前写过vue的简单项目,所以这次算是有一点经验的,但是之前的项目太简单,就4个单页,所以经验相对不足,可能新项目还会踩到很多坑。项目进度2019年03月04日已经搭建好项目框架,数据方面也开始自己构造,主要的技术难点也已经解决,预计工作时间5天:商品展示+商品详情:1天购物车:1天商品购买:1天订单列表+订单详情:1天个人中心+收货地址:1天即下周一完成第一版的商城项目。项目地址:vue-cli3-typescript算是立下flag了,但是工作中可能会临时接到其他需求,可能会打断我的进度,所以交付时间可能要押后3天左右。最迟2019年03月15日完成。至于后面的版本,如果原型出来了,就不能提交到公开的github上面了。结语未完待续……

March 4, 2019 · 1 min · jiezi

vue-cli3环境变量与分环境打包

第一步 : 了解环境变量概念我们可以根目录中的下列文件来指定环境变量:.env # 在所有的环境中被载入.env.local # 在所有的环境中被载入,但会被 git 忽略.env.[mode] # 只在指定的模式中被载入.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略环境变量文件只包含环境变量的“键=值”对:FOO=barVUE_APP_SECRET=secret // 只有VUE_APP_开头的环境变量可以在项目代码中直接使用除了 自定义的VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:NODE_ENV - 会是 “development”、“production” 或 “test” 中的一个。具体的值取决于应用运行的模式。BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。为一个特定模式准备的环境文件的 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。模式概念: 模式是 Vue CLI 项目中一个重要的概念。一般情况下 Vue CLI 项目有三个默认模式:development 模式用于 vue-cli-service serveproduction 模式用于 vue-cli-service build 和 vue-cli-service test:e2etest 模式用于 vue-cli-service test:unit模式不等同于 NODE_ENV,一个模式可以包含多个环境变量。也就是说,每个模式都将 NODE_ENV的值设置为模式的名称(可重新赋值更改)——比如在 development 模式下 NODE_ENV 的值会被设置为 “development”。 你可以通过为 .env 文件增加后缀来设置某个模式下特有的环境变量。比如,如果你在项目根目录创建一个名为 .env.development 的文件,那么在这个文件里声明过的变量就只会在 development 模式下被载入。你可以通过传递 –mode 选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量,请在你的 package.json 脚本中加入:“dev-build”: “vue-cli-service build –mode development”,环境变量的使用 : 只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中(即在项目代码中使用)。你可以在应用的代码中这样访问它们:console.log(process.env.VUE_APP_SECRET)理解指令 , 模式 , 环境变量之间的关系我们在项目中的package.json经常能看见以下这样的指令在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。vue-cli-service serve用法:vue-cli-service serve [options] [entry]选项: –open 在服务器启动时打开浏览器 –copy 在服务器启动时将 URL 复制到剪切版 –mode 指定环境模式 (默认值:development) –host 指定 host (默认值:0.0.0.0) –port 指定 port (默认值:8080) –https 使用 https (默认值:false)vue-cli-service build用法:vue-cli-service build [options] [entry|pattern]选项: –mode 指定环境模式 (默认值:production) –dest 指定输出目录 (默认值:dist) –modern 面向现代浏览器带自动回退地构建应用 –target app | lib | wc | wc-async (默认值:app) –name 库或 Web Components 模式下的名字 (默认值:package.json 中的 “name” 字段或入口文件名) –no-clean 在构建项目之前不清除目标目录 –report 生成 report.html 以帮助分析包内容 –report-json 生成 report.json 以帮助分析包内容 –watch 监听文件变化以上是两个常用的cli指令 , 他们默认对应的分别是development和production模式 , 如果还想了解其他指令 , 可以访问: https://cli.vuejs.org/zh/guid… CLI 服务那么接下来 , 我们就开始创建一个用于打包测试环境的模式;修改package.json添加一行命令"test": “vue-cli-service build –mode test"添加.env.test文件在项目根路径创建.env.test文件,内容为NODE_ENV=‘production’ //表明这是生产环境(需要打包)VUE_APP_CURRENTMODE=‘test’ // 表明生产环境模式信息VUE_APP_BASEURL=‘http://.*.com:8000’ // 测试服务器地址修改项目中的api接口文件在我的项目中,一般会创建一个api.js 来管理所有的接口url因为我们在本地开发环境中是通过代理来连接服务器的,所以将url写成这${baseUrl}/apis/v1/login,在文件开头通过环境变量改变baseUrllet baseUrl = ‘’;if (process.env.NODE_ENV == ‘development’) { baseUrl = "” } else if (process.env.NODE_ENV == ‘production’) { baseUrl = process.env.VUE_APP_BASEURL} else { baseUrl = "" }当需要为测试环境进行打包的时候 , 我们只需要运行下面指令进行打包npm run test ...

February 15, 2019 · 2 min · jiezi

vue-cli 3.0 源码分析

写在前面其实最开始不是特意来研究 vue-cli 的源码,只是想了解下 node 的命令,如果想要了解 node 命令的话,那么绕不开 tj 写的 commander.js。在学习 commander.js 过程中发现 vue-cli 的交互方式挺炫酷的,然后就去看了下源码,所以就有了这篇文章。链接vue-cli 3.0 源码分析目录@vue/cli前言介绍开始环境介绍常见 npm 包vue createcreate 入口整体分析基础验证获取预设选项(preset)依赖安装(installDeps)Generator结尾分析总结vue addadd 入口安装插件调用插件总结vue invokeinvoke 命令vue inspectinspect 命令vue serveserve 命令vue buildbuild 命令vue uiui 入口整体分析server 端client 端总结vue initinit 入口@vue/cli-init 分析vue-cli 2.x init 分析generate 函数分析总结vue configconfig 命令vue upgradeupgrade 命令vue infoinfo 命令@vue/cli-service整体介绍入口new Service()service.run()内置插件servebuildinspecthelp总结vue-cli-analysis 整个项目可大致分为两个部分,一部分是 vue 命令分析,包含 create,add,invoke, ui 等等,另一部分就是 vue-cli-service 的分析。通过分析发现与 2.X 相比,3.0 变化太大了,通过引入插件系统,可以让开发者利用其暴露的 API 对项目进行扩展。在分析之前对插件机制不是很了解,不知道如何实现的,分析之后逐渐了解了其实现机制,而且对于 vue 项目的配置也更加熟悉了。除此之外,在分析过程过程中还了解了很多有意思的 npm 包,比如 execa, debug, lowdb,lodash,inquirer 等等,最后,如果你想学习 node 命令或者想写一些比较有意思的命令行工具的话,阅读 vue-cli 源码是一个不错的选择。 ...

February 13, 2019 · 1 min · jiezi

vueSSR: 从0到1构建vueSSR项目 --- node以及vue-cli3的配置

前言上一次做了路由的相关配置,原本计划今天要做vuex部分,但是想了想,发现vuex单独的客户端部分穿插解释起来很麻烦,所以今天改做服务端部分。服务端部分做完,再去做vuex的部分,这样就会很清晰。vue ssr是分两个端,一个是客户端,一个是服务端。所以要做两个cli3的配置。那么下面就直接开始做吧。修改package.json的命令//package.json :client代表客户端 :server代表服务端//使用VUE_NODE来作为运行环境是node的标识//cli3内置 命令 –no-clean 不会清除dist文件 “scripts”: { “serve:client”: " vue-cli-service serve", “build”:“npm run build:server – –silent && npm run build:client – –no-clean –silent”, “build:client”: “vue-cli-service build”, “build:server”: “cross-env VUE_NODE=node vue-cli-service build”, “start:server”: “cross-env NODE_ENV=production nodemon nodeScript/index” }修改vue.config.js配置添加完相关脚本命令之后,我们开始改造cli3配置。首先要require(‘vue-server-renderer’)然后再根据VUE_NODE环境变量来决定编译的走向以及生成不同的环境清单先做cli3服务端的入口文件// src/entry/server.jsimport { createApp} from ‘../main.js’export default context => { return new Promise((resolve, reject) => { const { app, router } = createApp(context.data) //根据node传过来的路由 来调用router路由的指向 router.push(context.url) router.onReady(() => { //获取当前路由匹配的组件数组。 const matchedComponents = router.getMatchedComponents() //长度为0就是没找到该路由所匹配的组件 //可以路由设置重定向或者传回node node来操作也可以 if (!matchedComponents.length) { return reject({ code: 404 }) } resolve(app) }, reject) })}这里是cli3的配置//vue.config.jsconst ServerPlugin = require(‘vue-server-renderer/server-plugin’),//生成服务端清单 ClientPlugin = require(‘vue-server-renderer/client-plugin’),//生成客户端清单 nodeExternals = require(‘webpack-node-externals’),//忽略node_modules文件夹中的所有模块 VUE_NODE = process.env.VUE_NODE === ’node’, entry = VUE_NODE ? ‘server’ : ‘client’;//根据环境变量来指向入口module.exports = { css: { extract: false//关闭提取css,不关闭 node渲染会报错 }, configureWebpack: () => ({ entry: ./src/entry/${entry}, output: { filename: ‘js/[name].js’, chunkFilename: ‘js/[name].js’, libraryTarget: VUE_NODE ? ‘commonjs2’ : undefined }, target: VUE_NODE ? ’node’ : ‘web’, externals: VUE_NODE ? nodeExternals({ //设置白名单 whitelist: /.css$/ }) : undefined, plugins: [//根据环境来生成不同的清单。 VUE_NODE ? new ServerPlugin() : new ClientPlugin() ] }), chainWebpack: config => { config.resolve .alias .set(‘vue$’, ‘vue/dist/vue.esm.js’) config.module .rule(‘vue’) .use(‘vue-loader’) .tap(options => { options.optimizeSSR = false; return options; }); config.module .rule(‘images’) .use(‘url-loader’) .tap(options => { options = { limit: 1024, fallback:‘file-loader?name=img/[path][name].[ext]’ } return options; }); }}node相关配置用于node渲染 必然要拦截get请求的。然后根据get请求地址来进行要渲染的页面。官方提供了vue-server-renderer插件大概的方式就是 node拦截所有的get请求,然后将获取到的路由地址,传给前台,然后使用router实例进行push再往下面看之前 先看一下官方文档创建BundleRenderercreateBundleRenderer将 Vue 实例渲染为字符串。renderToString渲染应用程序的模板template生成所需要的客户端或服务端清单clientManifest先创建 服务端所需要的模板//public/index.nodeTempalte.html<!DOCTYPE html><html> <head> <meta http-equiv=“X-UA-Compatible” content=“IE=edge”> <meta name=“viewport” content=“width=device-width,initial-scale=1.0”> <link rel=“icon” href="/favicon.ico"> <meta charset=“utf-8”> <title>vuessr</title> </head> <body> <!–vue-ssr-outlet–> </body></html>node部分先创建三个文件index.js //入口proxy.js //代理server.js //主要配置//server.jsconst fs = require(‘fs’);const { resolve } = require(‘path’);const express = require(’express’);const app = express();const proxy = require(’./proxy’);const { createBundleRenderer } = require(‘vue-server-renderer’)//模板地址const templatePath = resolve(__dirname, ‘../public/index.nodeTempalte.html’)//客户端渲染清单const clientManifest = require(’../dist/vue-ssr-client-manifest.json’)//服务端渲染清单const bundle = require(’../dist/vue-ssr-server-bundle.json’)//读取模板const template = fs.readFileSync(templatePath, ‘utf-8’)const renderer = createBundleRenderer(bundle,{ template, clientManifest, runInNewContext: false})//代理相关proxy(app);//请求静态资源相关配置app.use(’/js’, express.static(resolve(__dirname, ‘../dist/js’)))app.use(’/css’, express.static(resolve(__dirname, ‘../dist/css’)))app.use(’/font’, express.static(resolve(__dirname, ‘../dist/font’)))app.use(’/img’, express.static(resolve(__dirname, ‘../dist/img’)))app.use(’.ico’, express.static(resolve(__dirname, ‘../dist’)))//路由请求app.get(’’, (req, res) => { res.setHeader(“Content-Type”, “text/html”) //传入路由 src/entry/server.js会接收到 使用vueRouter实例进行push const context = { url: req.url } renderer.renderToString(context, (err, html) => { if (err) { if (err.url) { res.redirect(err.url) } else { res.status(500).end(‘500 | 服务器错误’); console.error(${req.url}: 渲染错误 ); console.error(err.stack) } } res.status(context.HTTPStatus || 200) res.send(html) })})module.exports = app;//proxy.jsconst proxy = require(‘http-proxy-middleware’);function proxyConfig(obj){ return { target:’localhost:8081’, changeOrigin:true, …obj }}module.exports = (app) => { //代理开发环境 if (process.env.NODE_ENV !== ‘production’) { app.use(’/js/main*’, proxy(proxyConfig())); app.use(’/hot-update’,proxy(proxyConfig())); app.use(’/sockjs-node’,proxy(proxyConfig({ws:true}))); }}//index.jsconst app = require(’./server’)app.listen(8080, () => { console.log(’\033[42;37m DONE \033[40;33m localhost:8080 服务已启动\033[0m’)})做完这一步之后,就可以预览基本的服务渲染了。后面就只差开发环境的配置,以及到node数据的传递(vuex) npm run build npm run start:server 打开localhost:8080 F12 - Network - Doc 就可以看到内容最终目录结构|– vuessr |– .gitignore |– babel.config.js |– package-lock.json |– package.json |– README.md |– vue.config.js |– nodeScript //node 渲染配置 | |– index.js | |– proxy.js | |– server.js |– public//模板文件 | |– favicon.ico | |– index.html | |– index.nodeTempalte.html |– src |– App.vue |– main.js |– router.config.js//路由集合 |– store.config.js//vuex 集合 |– assets//全局静态资源源码 | |– 备注.txt | |– img | |– logo.png |– components//全局组件 | |– Head | |– index.js | |– index.scss | |– index.vue | |– img | |– logo.png |– entry//cli3入口 | |– client.js | |– server.js | |– 备注.txt |– methods//公共方法 | |– 备注.txt | |– mixin | |– index.js |– pages//源码目录 | |– home | | |– index.js | | |– index.scss | | |– index.vue | | |– img | | | |– flow.png | | | |– head_portrait.jpg | | | |– logo.png | | | |– vuessr.png | | |– vue | | | |– index.js | | | |– index.scss | | | |– index.vue | | |– vueCli3 | | | |– index.js | | | |– index.scss | | | |– index.vue | | |– vueSSR | | | |– index.js | | | |– index.scss | | | |– index.vue | | |– vuex | | |– index.js | | |– index.scss | | |– index.vue | |– router//路由配置 | | |– index.js | |– store//vuex配置 | |– all.js | |– gather.js | |– index.js |– static//cdn资源 |– 备注.txtgithub欢迎watch ...

January 28, 2019 · 3 min · jiezi

vueSSR: 从0到1构建vueSSR项目 --- 路由的构建

vue开发依赖的相关配置Vue SSR 指南今天先做客户端方面的配置,明天再做服务端的部分。那么马上开始吧~修改部分代码脚手架生成的代码肯定是不适合我们所用的 所以要修改一部分代码//App.vue<template> <div id=“app”> <router-view></router-view> </div></template><script>export default { name: ‘app’}</script><style> html,body,#app,#app>*{ width: 100%; height: 100%; } body{ font-family: ‘Avenir’, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; font-size: 16px; margin: 0; overflow-x: hidden; } img{ width: 200px; }</style>修改main.js为什么要这么做?为什么要避免状态单例main.js 是我们应用程序的「通用 entry」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实例,并直接挂载到 DOM。但是,对于服务器端渲染(SSR),责任转移到纯客户端 entry 文件。main.js 简单地使用 export 导出一个 createApp 函数:import Vue from ‘vue’;Vue.config.productionTip = false;import App from ‘./App.vue’;//router store 实例//这么做是避免状态单例export function createApp() { const app = new Vue({ //挂载router,store render: h => h(App) }) //暴露app实例 return { app };}添加Vue.config.js配置webpack的入口文件有两个,一个是客户端使用,一个是服务端使用。为何这么做?今天只做客户端部分。 src/vue.config.js module.exports = { css: { extract: false//关闭提取css,不关闭 node渲染会报错 }, configureWebpack: () => ({ entry: ‘./src/entry/client’ }) } app.$mount(’#app’)根目录创建 entry 文件夹,以及webpack入口代码 mdkir entry cd entry 创建 入口文件 client.js 作为客户端入口文件。 server,js 作为服务端端入口文件。 //先创建不做任何配置 entry/client.js import { createApp } from ‘../main.js’; const { app } = createApp(); app.$mount(’#app’);路由和代码分割官方说明的已经很清楚了,我就不做过多介绍了,下面直接展示代码添加新路由,这里将存放pages的相关路由src/pages/router/index.js/** * * @method componentPath 路由模块入口 * @param {string} name 要引入的文件地址 * @return {Object} /function componentPath (name = ‘home’){ return { component:() => import(../${name}/index.vue) }}export default [ { path: ‘/home’, …componentPath(), children: [ { path: “vue”, name: “vue”, …componentPath(‘home/vue’) }, { path: “vuex”, name: “vuex”, …componentPath(‘home/vuex’) }, { path: “vueCli3”, name: “vueCli3”, …componentPath(‘home/vueCli3’) }, { path: “vueSSR”, name: “vueSSR”, …componentPath(‘home/vueSSR’) } ] }]src/router.config.js作为路由的总配置 易于管理//路由总配置 import Vue from ‘vue’; import VueRouter from ‘vue-router’; Vue.use(VueRouter); //为什么采用这种做法。 //如果以后有了别的大模块可以单独开个文件夹与pages平级 //再这里导入即可。这样易于管理 // pages import pages from ‘./pages/router’; export function createRouter() { return new VueRouter({ mode: ‘history’, routes: [ { path: “”, redirect: ‘/home/vue’ }, …pages ] }) }更新main.jsimport Vue from ‘vue’;Vue.config.productionTip = false;import App from ‘./App.vue’;+ import { createRouter } from ‘./router.config.js’//router store 实例//这么做是避免状态单例export function createApp() {+ const router = createRouter() const app = new Vue({+ router, render: h => h(App) }) //暴露app,router实例 return { app, router };}更新 client.js由于使用的路由懒加载,所以必须要等路由提前解析完异步组件,才能正确地调用组件中可能存在的路由钩子。// client.jsimport { createApp } from ‘../main.js’;const { app, router } = createApp();router.onReady( () => { app.$mount(’#app’);})最近有点事情 vuex 部分后续再做 ...

January 25, 2019 · 2 min · jiezi

vueSSR: 从0到1构建vueSSR项目 --- 初始化

开始初始化 npm install -g @vue/cli nodemon nodemon 检测目录文件更改时,来重启基于node开发的程序 vue create vuessr 我选的附带 babel,eslint cd vuessr 创建文件以及文件夹 type null > vue.config.js //node相关配置文件 mkdir nodeScript cd nodeScript type null > index.js type null > proxy.js type null > server.js 进入src目录 //目录初始化 cd ../src type null > router.config.js //路由配置 mkdir pages //项目展示页面主要目录 cd pages mkdir router mkdir entry //vue-cli3 entry的相关配置入口 vueSSR需要。 mkdir static/js //gulp提取框架,插件等几年不动的源码整合后存放于cdn服务器 mkdir static/css //gulp提取整合初始化的样式表,存放的位置 mkdie methods //vue等全局代码的存放比如拦截器 use mixin 兼容函数 //安装依赖 npm install –save-dev sass-loader npm-run-all npm运行多个命令 -s 是顺序 -p是并行 cross-env 可以修改node环境变量 webpack-node-externals 忽略node_modules文件夹中的所有模块 vue-server-renderer 不解释修改eslint配置package.json eslintConfig rules 这个对象下面添加,cli的eslint附带以下的配置 所以手动关闭下。 “no-console”: 0, “no-unused-vars”: 0, “no-undef”: 0如果你觉得eslint警告很烦,那么可以 vue.config.js module.exports = { …, lintOnSave:false, … }明天开始配置 vue-router vuex entry 相关github欢迎watch ...

January 24, 2019 · 1 min · jiezi

一台阿里云主机 + one-sys脚手架 秒搭建自己的系统(博客、管理)

项目地址https://github.com/fanshyiis/…本脚手架主要致力于前端工程师的快速开发、一键部署等快捷开发框架,主要目的是想让前端工程师在一个阿里云服务器上可以快速开发部署自己的项目。本着前端后端融合统一的逻辑进行一些轮子的整合、并加入了自己的一些脚手架工具,第一次做脚手架的开发,如有问题,请在issue上提出,如果有帮助到您的地方,请不吝赐个star技术栈选择前端整合:vue-cli3.0、axios、element等命令行工具整合:commander、chalk、figlet、shelljs等后端整合:node、 koa2、koa-mysql-session、mysql等服务器整合:nginx、pm2、node等基本功能模块实现聚合分离所谓聚合分离,首先是‘聚合’,聚合代码,聚合插件,做到一个项目就可完成前端端代码的编写,打包上线等功能的聚合。其后是‘分离’。前后端分离。虽然代码会在同一个项目工程中但是前后端互不干扰,分别上线,区别于常规的ejs等服务端渲染的模式,做到前端完全分离一键部署基于本地的命令行工具,可以快速打包view端的静态文件并上传到阿里云服务器,也可快速上传server端的文件到服务器文件夹,配合pm2的监控功能进行代码的热更新,无缝更新接口逻辑快速迭代提供基本的使用案例,包括前端的view层的容器案例与组件案例,组件的api设定以及集合了axios的中间件逻辑,方便用户快速搭建自己的项目,代码清晰,易于分析与修改,server端对mysql连接池进行简单的封装,完成连接后及时释放,对table表格与函数进行分层,代码分层为路由层、控制器层、sql操作层基本模块举例1.登录页面登录 -正确反馈 错误反馈 登录成功后session的设定注册 -重名检测 正确反馈 错误反馈主要模块功能模块增删查改基本功能的实现后台koa2服务模块配合koa-mysql-session进行session的设定储存checkLogin中间件的实现cors跨域白名单的设定middlewer 中间件的设定mysql连接池的封装等等。。。服务端nginx 的基本配置与前端端分离的配置pm2 多实例构建配置文件的配置文件 pm2config.json使用流程本地调试安装mysql (过程请百度)// 进入sql命令行$ mysql -u root -p// 创建名为nodesql的数据库$ create database nodesql安装pm2 (过程请百度)拉取项目代码git clone https://github.com/fanshyiis/ONE-syscd ONE-sys// 安装插件cnpm i 或 npm i 或者 yarn add// 安装linksudo npm link// 然后就能使用命令行工具了one start// 或者不愿意使用命令行的同学可以yarn run serve主要代码解析代码逻辑serverbinone -h启动效果启动项目yarn run v1.3.2$ pm2 restart ./server/index.js && vue-cli-service serveUse –update-env to update environment variables[PM2] Applying action restartProcessId on app [./server/index.js](ids: 0,1)[PM2] index ✓[PM2] one-sys ✓┌──────────┬────┬─────────┬─────────┬───────┬────────┬─────────┬────────┬─────┬───────────┬───────────┬──────────┐│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │├──────────┼────┼─────────┼─────────┼───────┼────────┼─────────┼────────┼─────┼───────────┼───────────┼──────────┤│ index │ 0 │ 0.1.0 │ fork │ 77439 │ online │ 2640 │ 0s │ 0% │ 15.4 MB │ koala_cpx │ disabled ││ one-sys │ 1 │ 0.1.0 │ cluster │ 77438 │ online │ 15 │ 0s │ 0% │ 20.2 MB │ koala_cpx │ disabled │└──────────┴────┴─────────┴─────────┴───────┴────────┴─────────┴────────┴─────┴───────────┴───────────┴──────────┘ Use pm2 show &lt;id|name&gt; to get more details about an app INFO Starting development server… 98% after emitting CopyPlugin DONE Compiled successfully in 10294ms16:31:55 App running at: - Local: http://localhost:8080/ - Network: http://192.168.7.69:8080/ Note that the development build is not optimized. To create a production build, run yarn build.页面展示线上调试阿里云服务器文件存放目录[root@iZm5e6naugml8q0362d8rfZ ]# cd /home/[root@iZm5e6naugml8q0362d8rfZ home]# lsdist server test[root@iZm5e6naugml8q0362d8rfZ home]#阿里云nginx配置 location ^ /api { proxy_pass http://127.0.0.1:3000; } location ^ /redAlert/ { root /home/dist/; try_files $uri $uri/ /index.html =404; } location ^~ /file/ { alias /home/server/controller/public/; } location / { root /home/dist/; index index.html index.htm; }其他方面如同本地配置有问题可以加群联系最后请star一个吧~~~ ...

January 15, 2019 · 2 min · jiezi

vue-cli之vue-cli-service整体架构浅析

概述 vue启动一个项目的时候,需要执行npm run serve,其中这个serve的内容就是vue-cli-service serve。可见,项目的启动关键是这个vue-cli-service与它的参数serve。接下来我们一起看看service中主要写了什么东东(主要内容以备注形式写到代码中)。 关键代码 vue-cli-service.js const semver = require('semver')const { error } = require('@vue/cli-shared-utils')const requiredVersion = require('../package.json').engines.node// 检测node版本是否符合vue-cli运行的需求。不符合则打印错误并退出。if (!semver.satisfies(process.version, requiredVersion)) { error( `You are using Node ${process.version}, but vue-cli-service ` + `requires Node ${requiredVersion}.\nPlease upgrade your Node version.` ) process.exit(1)}// cli-service的核心类。const Service = require('../lib/Service')// 新建一个service的实例。并将项目路径传入。一般我们在项目根路径下运行该cli命令。所以process.cwd()的结果一般是项目根路径const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())// 参数处理。const rawArgv = process.argv.slice(2)const args = require('minimist')(rawArgv, { boolean: [ // build 'modern', 'report', 'report-json', 'watch', // serve 'open', 'copy', 'https', // inspect 'verbose' ]})const command = args._[0]// 将参数传入service这个实例并启动后续工作。如果我们运行的是npm run serve。则command = "serve"。service.run(command, args, rawArgv).catch(err => { error(err) process.exit(1)}) Service.js ...

January 14, 2019 · 3 min · jiezi

vue-cli系列之——vue-cli自身引用了哪些包?持续更新中……

概述当vue-cli创建了一个vue-demo后,我们使用npm run serve就可以启动项目了。通过package.json中的serve命令我们可以看到,实际是启动了vue-cli-service serve这个命令。这个命令实际上是启动了一段node脚本,那这个脚本引用了哪些包呢?让我们来一探究竟。semver概述:这个包是用来做版本号规范处理的,可以校验一个输入的版本号是否符合规范,以及某个包是否满足某个版本。实例:检测本机node版本是否符合vue-cli需求的node版本。if (!semver.satisfies(process.version, requiredVersion)) { error( You are using Node ${process.version}, but vue-cli-service + requires Node ${requiredVersion}.\nPlease upgrade your Node version. ) process.exit(1)}npm 链接:https://www.npmjs.com/package…minimist概述:这个包是用来处理命令行的参数输入的。实例:检测命令行参数中是否有如下几个参数const args = require(‘minimist’)(rawArgv, { boolean: [ // build ‘modern’, ‘report’, ‘report-json’, ‘watch’, // serve ‘open’, ‘copy’, ‘https’, // inspect ‘verbose’ ]})npm链接:https://www.npmjs.com/package…debug概述: 一个轻量级的调试工具,根据参数返回一个打印错误的函数。看起来是可以让不同的错误输出更清晰,颜色很美。实例: 为不同的错误创建不同的错误输出函数const logger = debug(‘vue:env’); //调用该函数会输出以vue:env开头的字符串。//…此处省略好多业务代码debug(‘vue:project-config’)(this.projectOptions) // 输出vue:project-config开头的字符串。npm链接:https://www.npmjs.com/package…chalk概述:可以以不同的样式输出字符串到终端实例:打印不一段字符串,其中关键字加粗if (fileConfig) { if (pkgConfig) { warn( "vue" field in package.json ignored + due to presence of ${chalk.bold('vue.config.js')}. ) warn( You should migrate it into ${chalk.bold('vue.config.js')} + and remove it from package.json. ) } resolved = fileConfig resolvedFrom = ‘vue.config.js’}npm链接:https://www.npmjs.com/package…joi概述:一个js类型&字段验证的库。类似schemas的概念。实例:vue-cli中为了确保配置对象的正确性,定义了一个蓝本,去校验配置的正确性(主要是校验用户配置,例如vue.config.js中的输入)const schema = createSchema(joi => joi.object({ baseUrl: joi.string().allow(’’), publicPath: joi.string().allow(’’), outputDir: joi.string(), assetsDir: joi.string().allow(’’), indexPath: joi.string(), filenameHashing: joi.boolean(), runtimeCompiler: joi.boolean(), transpileDependencies: joi.array(), productionSourceMap: joi.boolean(), parallel: joi.boolean(), devServer: joi.object(), pages: joi.object().pattern( /\w+/, joi.alternatives().try([ joi.string(), joi.object().keys({ entry: joi.string().required() }).unknown(true) ]) ), crossorigin: joi.string().valid([’’, ‘anonymous’, ‘use-credentials’]), integrity: joi.boolean(), // css css: joi.object({ modules: joi.boolean(), extract: joi.alternatives().try(joi.boolean(), joi.object()), sourceMap: joi.boolean(), loaderOptions: joi.object({ css: joi.object(), sass: joi.object(), less: joi.object(), stylus: joi.object(), postcss: joi.object() }) }), // webpack chainWebpack: joi.func(), configureWebpack: joi.alternatives().try( joi.object(), joi.func() ), // known runtime options for built-in plugins lintOnSave: joi.any().valid([true, false, ’error’]), pwa: joi.object(), // 3rd party plugin options pluginOptions: joi.object()}))npm链接:https://www.npmjs.com/package…lodash.defaultsDeep概述:lodash这个包提供的一个函数,有点类似Object.assign()这个函数,可以用来默认参数填充。实例:vue中,合并用户配置(vue.config.js)与默认配置。// lodash官方案例:.defaultsDeep({ ‘a’: { ‘b’: 2 } }, { ‘a’: { ‘b’: 1, ‘c’: 3 } });// => { ‘a’: { ‘b’: 2, ‘c’: 3 } }// vue-cli中的应用this.projectOptions = defaultsDeep(userOptions, defaults());// 这里贴上vue-cli的默认配置exports.defaults = () => ({ // project deployment base publicPath: ‘/’, // where to output built files outputDir: ‘dist’, // where to put static assets (js/css/img/font/…) assetsDir: ‘’, // filename for index.html (relative to outputDir) indexPath: ‘index.html’, // whether filename will contain hash part filenameHashing: true, // boolean, use full build? runtimeCompiler: false, // deps to transpile transpileDependencies: [/* string or regex */], // sourceMap for production build? productionSourceMap: !process.env.VUE_CLI_TEST, // use thread-loader for babel & TS in production build // enabled by default if the machine has more than 1 cores parallel: hasMultipleCores(), // multi-page config pages: undefined, // <script type=“module” crossorigin=“use-credentials”> // #1656, #1867, #2025 crossorigin: undefined, // subresource integrity integrity: false, css: { // extract: true, // modules: false, // localIdentName: ‘[name][local]_[hash:base64:5]’, // sourceMap: false, // loaderOptions: {} }, // whether to use eslint-loader lintOnSave: true, devServer: { /* open: process.platform === ‘darwin’, host: ‘0.0.0.0’, port: 8080, https: false, hotOnly: false, proxy: null, // string | Object before: app => {} */ }}) ...

January 13, 2019 · 2 min · jiezi

vue-cli3使用svg问题的简单解决办法

此解决办法使用的是 vue-cli 插件 vue-cli-plugin-svg-sprite效果如下:那个朋友圈图标就是我从网上找的svg图片使用方式如下:vue add svg-spritevue.config.js添加配置,在文件内最下方找到pluginOptionsmodule.exports = { pluginOptions: { svgSprite: { /* * The directory containing your SVG files. / dir: ‘src/assets/icons’, / * The reqex that will be used for the Webpack rule. / test: /.(svg)(?.)?$/, /* * @see https://github.com/kisenka/svg-sprite-loader#configuration / loaderOptions: { extract: true, spriteFilename: ‘img/icons.[hash:8].svg’ // or ‘img/icons.svg’ if filenameHashing == false }, / * @see https://github.com/kisenka/svg-sprite-loader#configuration */ pluginOptions: { plainSprite: true } } } };再执行:npm install svgo svgo-loader –save-dev然后再在 your vue.config.js file:module.exports = { chainWebpack: config => { config.module .rule(‘svg-sprite’) .use(‘svgo-loader’) .loader(‘svgo-loader’); }};然后再assets下创建icons文件夹,将你的svg图标放入,name就是svg的名字,如下方式使用:<svg-icon name=“aperture”></svg-icon>这个组件是插件帮你生成的就会看到你的svg图标出来了 ...

January 13, 2019 · 1 min · jiezi

productionSourceMap: false

January 3, 2019 · 0 min · jiezi

Vue CLI 3 多页应用项目的搭建

在项目初期时,从零开始搭建和配置本地前端开发环境是一项很繁琐的工作,需要考虑到项目目录结构、项目基本配置、Webpack 配置等等。通过 Vue CLI 3 可以快速的生成一个项目,这样我们就可以专注在写应用上,而不必将大量时间花费在配置上。但是官方的 vue create 命令只能快速地建立一个单页应用的原型,想要开发多页应用还需要再配置一些东西。可同时支持less,sass。说明Vue CLI 3 多页应用项目**Git地址: https://github.com/zydemail/v…。初始化git clone https://github.com/zydemail/vue-mpa.gitcd vue-mpanpm install使用开发:本地运行:npm run serve:dev 注:本地开发调试服务器接口需开启浏览器跨域,可在 Chrome 应用商店安装 Allow CORS: Access-Control-Allow-Origin 插件即可解决修复代码格式错误:npm run lintfix打包:开发环境: npm run dev测试环境: npm run build:tests生产环境: npm run build:prod目录结构说明│ .browserslistrc // 配置目标浏览器│ .env.development // 开发环境配置,可在此配置全局变量,使用 process.env 读取,打包工具会根据不同环境自动读取变量│ .env.localdev // 本地开发环境配置,可在此配置全局变量,使用 process.env 读取,打包工具会根据不同环境自动读取变量│ .env.production // 生产环境配置,可在此配置全局变量,使用 process.env 读取,打包工具会根据不同环境自动读取变量│ .env.tests // 测试环境配置,可在此配置全局变量,使用 process.env 读取,打包工具会根据不同环境自动读取变量│ .eslintrc.js // eslint 配置│ .gitignore│ .postcssrc.js // postcss配置,一般不会用到,使用默认值│ babel.config.js // babel配置,一般不会用到,使用默认值│ package-lock.json│ package.json│ README.md│ title.js // 统一配置生成页面的 title│ vue.config.js // vue 配置,可配置 webpack 等,可参照 https://cli.vuejs.org/zh/config/│ ├─public // 此文件夹下可以放置一些静态资源,除了index.html会经过处理外,其他文件都会原封不动的自动复制到 htdocs 根目录下,不会经过 webpack 的处理。│ favicon.ico│ index.html // 所有的打包页面都会经过这个文件,本模板对多页的配置采用了统一处理,当然也可以在 vue.config.js 单独配置每个页面,可参照 https://cli.vuejs.org/zh/config/#pages│ ├─src // 源代码文件夹│ ├─assets // 资源文件夹,可放置 css、images等│ │ logo.png│ │ │ ├─components // 组件文件夹,可定义一些公共组件│ │ Header.vue│ │ │ ├─pages // 页面文件夹,每个页面都是一个文件夹│ │ ├─about // about 页面│ │ │ app.js // 一般不做更改│ │ │ app.vue // 页面的 html、css、js 都写在这个文件里│ │ │ │ │ ├─index│ │ │ app.js│ │ │ app.vue│ │ │ │ │ └─user│ │ └─index│ │ app.js│ │ app.vue│ │ │ ├─style // 公共样式文件夹,可以定义一些公共样式,如浏览器重置样式 reset.less,此文件夹可按需求随意更改 │ │ │ index.less│ │ │ │ │ └─core│ │ index.less│ │ reset.less│ │ │ └─utils // 常用 js 工具类│ └─core│ http.js // http 请求库,封装 axios,可直接调用│ └─tests // 单元测试,可忽略 └─unit .eslintrc.js ...

December 29, 2018 · 1 min · jiezi

webpack-chain项目中文翻译

webpack-chain注意:这是对原项目readme文件的翻译,为啥翻译这个呢,因为Vue CLI3脚手架生成的项目使用这种方式配置webpack,但是脚手架中对这块的介绍不多,所以把这部分翻译出来,以供团队和大家参考。应用一个链式 API 来生成和简化 2-4 版本的webpack的配置的修改。此文档对应于webpack-chain的v5版本,对于以前的版本,请参阅:v4 docsv3 docsv2 docsv1 docs注意: 虽然 webpack-chain 被广泛应用在Neutrino中,然而本软件包完全独立,可供任何项目使用。介绍webpack 的核心配置的创建和修改基于一个有潜在难于处理的 JavaScript 对象。虽然这对于配置单个项目来说还是 OK 的,但当你尝试跨项目共享这些对象并使其进行后续的修改就会变的混乱不堪,因为您需要深入了解底层对象的结构以进行这些更改。webpack-chain 尝试通过提供可链式或顺流式的 API 创建和修改webpack 配置。API的 Key 部分可以由用户指定的名称引用,这有助于 跨项目修改配置方式 的标准化。通过以下示例可以更容易地解释这一点。安装webpack-chain 需要 Node.js v6.9及更高版本. webpack-chain 也只创建并被设计于使用webpack的2,3,4版本的配置对象。你可以使用Yarn或者npm来安装此软件包(俩个包管理工具选一个就行):Yarn方式yarn add –dev webpack-chainnpm方式npm install –save-dev webpack-chain入门当你安装了 webpack-chain, 你就可以开始创建一个webpack的配置。 对于本指南,我们的示例基本配置 webpack.config.js 将位于我们项目的根目录。// 导入 webpack-chain 模块,该模块导出了一个用于创建一个webpack配置API的单一构造函数。const Config = require(‘webpack-chain’);// 对该单一构造函数创建一个新的配置实例const config = new Config();// 用链式API改变配置// 每个API的调用都会跟踪对存储配置的更改。config // 修改 entry 配置 .entry(‘index’) .add(‘src/index.js’) .end() // 修改 output 配置 .output .path(‘dist’) .filename(’[name].bundle.js’);// 创建一个具名规则,以后用来修改规则config.module .rule(’lint’) .test(/.js$/) .pre() .include .add(‘src’) .end() // 还可以创建具名use (loaders) .use(’eslint’) .loader(’eslint-loader’) .options({ rules: { semi: ‘off’ } });config.module .rule(‘compile’) .test(/.js$/) .include .add(‘src’) .add(’test’) .end() .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [ [’@babel/preset-env’, { modules: false }] ] });// 也可以创建一个具名的插件!config .plugin(‘clean’) .use(CleanPlugin, [[‘dist’], { root: ‘/dir’ }]);// 导出这个修改完成的要被webpack使用的配置对象module.exports = config.toConfig();共享配置也很简单。仅仅导出配置 和 在传递给webpack之前调用 .toConfig() 方法将配置导出给webpack使用。// webpack.core.jsconst Config = require(‘webpack-chain’);const config = new Config();// 跨目标共享配置// Make configuration shared across targets// …module.exports = config;// webpack.dev.jsconst config = require(’./webpack.core’);// Dev-specific configuration// 开发具体配置// …module.exports = config.toConfig();// webpack.prod.jsconst config = require(’./webpack.core’);// Production-specific configuration// 生产具体配置// …module.exports = config.toConfig();ChainedMapwebpack-chain 中的核心API接口之一是 ChainedMap. 一个 ChainedMap的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedMap, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedMap , 允许链式调用这些方法。// 从 Map 移除所有 配置.clear()// 通过键值从 Map 移除单个配置.// key: *delete(key)// 获取 Map 中相应键的值// key: *// returns: valueget(key)// 获取 Map 中相应键的值// 如果键在Map中不存在,则ChainedMap中该键的值会被配置为fn的返回值.// key: *// fn: Function () -> value// returns: valuegetOrCompute(key, fn)// 配置Map中 已存在的键的值// key: *// value: *set(key, value)// Map中是否存在一个配置值的特定键,返回 真或假// key: *// returns: Booleanhas(key)// 返回 Map中已存储的所有值的数组// returns: Arrayvalues()// 返回Map中全部配置的一个对象, 其中 键是这个对象属性,值是相应键的值,// 如果Map是空,返回 undefined// 使用 .before() 或 .after() 的ChainedMap, 则将按照属性名进行排序。// returns: Object, undefined if emptyentries()// 提供一个对象,这个对象的属性和值将 映射进 Map。// 你也可以提供一个数组作为第二个参数以便忽略合并的属性名称。// obj: Object// omit: Optional Arraymerge(obj, omit)// 对当前配置上下文执行函数。// handler: Function -> ChainedMap // 一个把ChainedMap实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedMap // 当条件为真,调用把ChainedMap实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedMap // 当条件为假,调用把ChainedMap实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)ChainedSetwebpack-chain 中的核心API接口另一个是 ChainedSet. 一个 ChainedSet的操作类似于JavaScript Map, 为链式和生成配置提供了一些便利。 如果一个属性被标记一个 ChainedSet, 则它将具有如下的API和方法:除非另有说明,否则这些方法将返回 ChainedSet , 允许链式调用这些方法。// 添加/追加 给Set末尾位置一个值.// value: *add(value)// 添加 给Set开始位置一个值.// value: prepend(value)// 移除Set中全部值.clear()// 移除Set中一个指定的值.// value: delete(value)// 检测Set中是否存在一个值.// value: // returns: Booleanhas(value)// 返回Set中值的数组.// returns: Arrayvalues()// 连接给定的数组到 Set 尾部。// arr: Arraymerge(arr)// 对当前配置上下文执行函数。// handler: Function -> ChainedSet // 一个把 ChainedSet 实例作为单个参数的函数batch(handler)// 条件执行一个函数去继续配置// condition: Boolean// whenTruthy: Function -> ChainedSet // 当条件为真,调用把 ChainedSet 实例作为单一参数传入的函数// whenFalsy: Optional Function -> ChainedSet // 当条件为假,调用把 ChainedSet 实例作为单一参数传入的函数when(condition, whenTruthy, whenFalsy)速记方法存在许多简写方法,用于 使用与简写方法名称相同的键在 ChainedMap 设置一个值例如, devServer.hot 是一个速记方法, 因此它可以用作:// 在 ChainedMap 上设置一个值的 速记方法devServer.hot(true);// 上述方法等效于:devServer.set(‘hot’, true);一个速记方法是可链式的,因此调用它将返回 原实例,允许你继续链式使用配置创建一个新的配置对象const Config = require(‘webpack-chain’);const config = new Config();移动到API的更深层将改变你正在修改的内容的上下文。 你可以通过 config在此引用顶级配置或者通过调用 .end() 方法向上移动一级 使你移回更高的 上下文环境。如果你熟悉jQuery, 这里与其 .end() 工作原理类似。除非另有说明,否则全部的API调用都将在当前上下文中返回API实例。 这样,你可以根据需要连续 链式API调用. 有关对所有速记和低级房费有效的特定值的详细信息,请参阅 webpack文档层次结构 中的相应名词。Config : ChainedMap配置速记方法config .amd(amd) .bail(bail) .cache(cache) .devtool(devtool) .context(context) .externals(externals) .loader(loader) .mode(mode) .parallelism(parallelism) .profile(profile) .recordsPath(recordsPath) .recordsInputPath(recordsInputPath) .recordsOutputPath(recordsOutputPath) .stats(stats) .target(target) .watch(watch) .watchOptions(watchOptions)配置 entryPoints// 回到 config.entryPoints : ChainedMapconfig.entry(name) : ChainedSetconfig .entry(name) .add(value) .add(value)config .entry(name) .clear()// 用低级别 config.entryPoints:config.entryPoints .get(name) .add(value) .add(value)config.entryPoints .get(name) .clear()配置 output: 速记 方法config.output : ChainedMapconfig.output .auxiliaryComment(auxiliaryComment) .chunkFilename(chunkFilename) .chunkLoadTimeout(chunkLoadTimeout) .crossOriginLoading(crossOriginLoading) .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate) .devtoolLineToLine(devtoolLineToLine) .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate) .filename(filename) .hashFunction(hashFunction) .hashDigest(hashDigest) .hashDigestLength(hashDigestLength) .hashSalt(hashSalt) .hotUpdateChunkFilename(hotUpdateChunkFilename) .hotUpdateFunction(hotUpdateFunction) .hotUpdateMainFilename(hotUpdateMainFilename) .jsonpFunction(jsonpFunction) .library(library) .libraryExport(libraryExport) .libraryTarget(libraryTarget) .path(path) .pathinfo(pathinfo) .publicPath(publicPath) .sourceMapFilename(sourceMapFilename) .sourcePrefix(sourcePrefix) .strictModuleExceptionHandling(strictModuleExceptionHandling) .umdNamedDefine(umdNamedDefine)配置 resolve(解析): 速记方法config.resolve : ChainedMapconfig.resolve .cachePredicate(cachePredicate) .cacheWithContext(cacheWithContext) .enforceExtension(enforceExtension) .enforceModuleExtension(enforceModuleExtension) .unsafeCache(unsafeCache) .symlinks(symlinks)配置 resolve 别名config.resolve.alias : ChainedMapconfig.resolve.alias .set(key, value) .set(key, value) .delete(key) .clear()配置 resolve modulesconfig.resolve.modules : ChainedSetconfig.resolve.modules .add(value) .prepend(value) .clear()配置 resolve aliasFieldsconfig.resolve.aliasFields : ChainedSetconfig.resolve.aliasFields .add(value) .prepend(value) .clear()配置 resolve descriptionFieldsconfig.resolve.descriptionFields : ChainedSetconfig.resolve.descriptionFields .add(value) .prepend(value) .clear()配置 resolve extensionsconfig.resolve.extensions : ChainedSetconfig.resolve.extensions .add(value) .prepend(value) .clear()配置 resolve mainFieldsconfig.resolve.mainFields : ChainedSetconfig.resolve.mainFields .add(value) .prepend(value) .clear()配置 resolve mainFilesconfig.resolve.mainFiles : ChainedSetconfig.resolve.mainFiles .add(value) .prepend(value) .clear()配置 resolveLoader当前API config.resolveLoader 相同于 配置 config.resolve 用下面的配置:配置 resolveLoader moduleExtensionsconfig.resolveLoader.moduleExtensions : ChainedSetconfig.resolveLoader.moduleExtensions .add(value) .prepend(value) .clear()配置 resolveLoader packageMainsconfig.resolveLoader.packageMains : ChainedSetconfig.resolveLoader.packageMains .add(value) .prepend(value) .clear()配置 performance(性能): 速记方法config.performance : ChainedMapconfig.performance .hints(hints) .maxEntrypointSize(maxEntrypointSize) .maxAssetSize(maxAssetSize) .assetFilter(assetFilter)配置 optimizations(优化): 速记方法config.optimization : ChainedMapconfig.optimization .concatenateModules(concatenateModules) .flagIncludedChunks(flagIncludedChunks) .mergeDuplicateChunks(mergeDuplicateChunks) .minimize(minimize) .namedChunks(namedChunks) .namedModules(namedModules) .nodeEnv(nodeEnv) .noEmitOnErrors(noEmitOnErrors) .occurrenceOrder(occurrenceOrder) .portableRecords(portableRecords) .providedExports(providedExports) .removeAvailableModules(removeAvailableModules) .removeEmptyChunks(removeEmptyChunks) .runtimeChunk(runtimeChunk) .sideEffects(sideEffects) .splitChunks(splitChunks) .usedExports(usedExports)配置 optimization minimizers(最小优化器)// 回到 config.optimization.minimizersconfig.optimization .minimizer(name) : ChainedMap配置 optimization minimizers: 添加注意: 不要用 new 去创建最小优化器插件,因为已经为你做好了。config.optimization .minimizer(name) .use(WebpackPlugin, args)// 例如config.optimization .minimizer(‘css’) .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])// Minimizer 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config.optimization .minimizer(‘css’) .use(require.resolve(‘optimize-css-assets-webpack-plugin’), [{ cssProcessorOptions: { safe: true } }])配置 optimization minimizers: 修改参数config.optimization .minimizer(name) .tap(args => newArgs)// 例如config .minimizer(‘css’) .tap(args => […args, { cssProcessorOptions: { safe: false } }])配置 optimization minimizers: 修改实例config.optimization .minimizer(name) .init((Plugin, args) => new Plugin(…args));配置 optimization minimizers: 移除config.optimization.minimizers.delete(name)配置插件// 回到 config.pluginsconfig.plugin(name) : ChainedMap配置插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config .plugin(name) .use(WebpackPlugin, args)// 例如config .plugin(‘hot’) .use(webpack.HotModuleReplacementPlugin);// 插件也可以由它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require s。config .plugin(’env’) .use(require.resolve(‘webpack/lib/EnvironmentPlugin’), [{ ‘VAR’: false }]);配置插件: 修改参数config .plugin(name) .tap(args => newArgs)// 例如config .plugin(’env’) .tap(args => […args, ‘SECRET_KEY’]);配置插件: 修改实例config .plugin(name) .init((Plugin, args) => new Plugin(…args));配置插件: 移除config.plugins.delete(name)配置插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .before(otherName)// 例如config .plugin(‘html-template’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin) .before(‘html-template’);Config plugins: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config .plugin(name) .after(otherName)// 例如config .plugin(‘html-template’) .after(‘script-ext’) .use(HtmlWebpackTemplate) .end() .plugin(‘script-ext’) .use(ScriptExtWebpackPlugin);配置 resolve 插件// 回到 config.resolve.pluginsconfig.resolve.plugin(name) : ChainedMap配置 resolve 插件: 添加注意: 不要用 new 去创建插件,因为已经为你做好了。config.resolve .plugin(name) .use(WebpackPlugin, args)配置 resolve 插件: 修改参数config.resolve .plugin(name) .tap(args => newArgs)配置 resolve 插件: 修改实例config.resolve .plugin(name) .init((Plugin, args) => new Plugin(…args))配置 resolve 插件: 移除config.resolve.plugins.delete(name)配置 resolve 插件: 在之前调用指定当前插件上下文应该在另一个指定插件之前执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .before(otherName)// 例如config.resolve .plugin(‘beta’) .use(BetaWebpackPlugin) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin) .before(‘beta’);配置 resolve 插件: 在之后调用指定当前插件上下文应该在另一个指定插件之后执行,你不能在同一个插件上同时使用 .before() 和 .after()。config.resolve .plugin(name) .after(otherName)// 例如config.resolve .plugin(‘beta’) .after(‘alpha’) .use(BetaWebpackTemplate) .end() .plugin(‘alpha’) .use(AlphaWebpackPlugin);配置 nodeconfig.node : ChainedMapconfig.node .set(’__dirname’, ‘mock’) .set(’__filename’, ‘mock’);配置 devServerconfig.devServer : ChainedMap配置 devServer allowedHostsconfig.devServer.allowedHosts : ChainedSetconfig.devServer.allowedHosts .add(value) .prepend(value) .clear()配置 devServer: 速记方法config.devServer .bonjour(bonjour) .clientLogLevel(clientLogLevel) .color(color) .compress(compress) .contentBase(contentBase) .disableHostCheck(disableHostCheck) .filename(filename) .headers(headers) .historyApiFallback(historyApiFallback) .host(host) .hot(hot) .hotOnly(hotOnly) .https(https) .inline(inline) .info(info) .lazy(lazy) .noInfo(noInfo) .open(open) .openPage(openPage) .overlay(overlay) .pfx(pfx) .pfxPassphrase(pfxPassphrase) .port(port) .progress(progress) .proxy(proxy) .public(public) .publicPath(publicPath) .quiet(quiet) .setup(setup) .socket(socket) .staticOptions(staticOptions) .stats(stats) .stdin(stdin) .useLocalIp(useLocalIp) .watchContentBase(watchContentBase) .watchOptions(watchOptions)配置 moduleconfig.module : ChainedMap配置 module: 速记方法config.module : ChainedMapconfig.module .noParse(noParse)配置 module rules: 速记方法config.module.rules : ChainedMapconfig.module .rule(name) .test(test) .pre() .post() .enforce(preOrPost)配置 module rules uses (loaders): 创建config.module.rules{}.uses : ChainedMapconfig.module .rule(name) .use(name) .loader(loader) .options(options)// Exampleconfig.module .rule(‘compile’) .use(‘babel’) .loader(‘babel-loader’) .options({ presets: [’@babel/preset-env’] });配置 module rules uses (loaders): 修改选项config.module .rule(name) .use(name) .tap(options => newOptions)// 例如config.module .rule(‘compile’) .use(‘babel’) .tap(options => merge(options, { plugins: [’@babel/plugin-proposal-class-properties’] }));配置 module rules oneOfs (条件 rules)config.module.rules{}.oneOfs : ChainedMap<Rule>config.module .rule(name) .oneOf(name)// 例如config.module .rule(‘css’) .oneOf(‘inline’) .resourceQuery(/inline/) .use(‘url’) .loader(‘url-loader’) .end() .end() .oneOf(’external’) .resourceQuery(/external/) .use(‘file’) .loader(‘file-loader’)合并配置webpack-chain 支持将对象合并到配置实例,改实例类似于 webpack-chain 模式 布局的布局。 请注意,这不是 webpack 配置对象,但您可以再将webpack配置对象提供给webpack-chain 以匹配器布局之前对其进行转换。config.merge({ devtool: ‘source-map’ });config.get(‘devtool’) // “source-map"config.merge({ [key]: value, amd, bail, cache, context, devtool, externals, loader, mode, parallelism, profile, recordsPath, recordsInputPath, recordsOutputPath, stats, target, watch, watchOptions, entry: { [name]: […values] }, plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } }, devServer: { [key]: value, clientLogLevel, compress, contentBase, filename, headers, historyApiFallback, host, hot, hotOnly, https, inline, lazy, noInfo, overlay, port, proxy, quiet, setup, stats, watchContentBase }, node: { [key]: value }, optimizations: { concatenateModules, flagIncludedChunks, mergeDuplicateChunks, minimize, minimizer, namedChunks, namedModules, nodeEnv, noEmitOnErrors, occurrenceOrder, portableRecords, providedExports, removeAvailableModules, removeEmptyChunks, runtimeChunk, sideEffects, splitChunks, usedExports, }, performance: { [key]: value, hints, maxEntrypointSize, maxAssetSize, assetFilter }, resolve: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, resolveLoader: { [key]: value, alias: { [key]: value }, aliasFields: […values], descriptionFields: […values], extensions: […values], mainFields: […values], mainFiles: […values], modules: […values], moduleExtensions: […values], packageMains: […values], plugin: { [name]: { plugin: WebpackPlugin, args: […args], before, after } } }, module: { [key]: value, rule: { [name]: { [key]: value, enforce, issuer, parser, resource, resourceQuery, test, include: […paths], exclude: […paths], oneOf: { [name]: Rule }, use: { [name]: { loader: LoaderString, options: LoaderOptions, before, after } } } } }})条件配置当使用的情况下工作ChainedMap和ChainedSet,则可以使用执行条件的配置when。您必须指定一个表达式 when(),以评估其真实性或虚假性。如果表达式是真实的,则将使用当前链接实例的实例调用第一个函数参数。您可以选择提供在条件为假时调用的第二个函数,该函数也是当前链接的实例。// 示例:仅在生产期间添加minify插件config .when(process.env.NODE_ENV === ‘production’, config => { config .plugin(‘minify’) .use(BabiliWebpackPlugin); });// 例:只有在生产过程中添加缩小插件,否则设置devtool到源映射config .when(process.env.NODE_ENV === ‘production’, config => config.plugin(‘minify’).use(BabiliWebpackPlugin), config => config.devtool(‘source-map’) );检查生成的配置您可以使用检查生成的webpack配置config.toString()。这将生成配置的字符串化版本,其中包含命名规则,用法和插件的注释提示:config .module .rule(‘compile’) .test(/.js$/) .use(‘babel’) .loader(‘babel-loader’);config.toString();{ module: { rules: [ / config.module.rule(‘compile’) / { test: /.js$/, use: [ / config.module.rule(‘compile’).use(‘babel’) / { loader: ‘babel-loader’ } ] } ] }}默认情况下,如果生成的字符串包含需要的函数和插件,则不能直接用作真正的webpack配置。为了生成可用的配置,您可以通过__expression在其上设置特殊属性来自定义函数和插件的字符串化方式:class MyPlugin {}MyPlugin.__expression = require('my-plugin');function myFunction () {}myFunction.__expression = require('my-function');config .plugin(’example’) .use(MyPlugin, [{ fn: myFunction }]);config.toString();/{ plugins: [ new (require(‘my-plugin’))({ fn: require(‘my-function’) }) ]}/通过其路径指定的插件将require()自动生成其语句:config .plugin(’env’) .use(require.resolve(‘webpack/lib/ProvidePlugin’), [{ jQuery: ‘jquery’ }])config.toString();{ plugins: [ new (require(’/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js’))( { jQuery: ‘jquery’ } ) ]}您还可以调用toString静态方法Config,以便在字符串化之前修改配置对象。Config.toString({ …config.toConfig(), module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ }, }, ], }, ], },}){ plugins: [ / config.plugin(‘foo’) */ new TestPlugin() ], module: { defaultRules: [ { use: [ { loader: ‘banner-loader’, options: { prefix: ‘banner-prefix.txt’ } } ] } ] }} ...

December 28, 2018 · 6 min · jiezi