关于Vue2一些值得推荐的文章 -- 十二月份

十二月份查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。十二月上半月-幽州思妇十二月,停歌罢笑双蛾摧。(12.01~12.15):北风行北风行 [唐] 李白 烛龙栖寒门,光曜犹旦开。 日月照之何不及此,唯有北风号怒天上来。 燕山雪花大如席,片片吹落轩辕台。 幽州思妇十二月,停歌罢笑双蛾摧。 倚门望行人,念君长城苦寒良可哀。 别时提剑救边去,遗此虎纹金鞞靫。 中有一双白羽箭,蜘蛛结网生尘埃。 箭空在,人今战死不复回。 不忍见此物,焚之已成灰。 黄河捧土尚可塞,北风雨雪恨难裁。Vue中文推荐列表加快Vue项目的开发速度2019年Vue学习路线图Vue 性能优化之深挖数组几种常见的Vue组件间的传参方式前端错误收集(Vue.js、微信小程序)vue3.0 尝鲜 – 摒弃 Object.defineProperty,基于 Proxy 的观察者机制探索说说在 Vue.js 中如何实现组件间通信(高级篇)不吹不黑比对下React与Vue的差异与优劣尤雨溪:React 是不是比 Vue 牛,为什么?初探 Vue3.0 中的一大亮点——Proxyvue权限路由实现方式总结二从零实现Vue的组件库(零)-基本结构以及构建工具Vue 2.0学习笔记:Vue的transition推荐一个很好用的vscode插件:一个可以给出vuex中store定义信息的vscode插件vue轻量高效的前端组件化方案以及MVC MVVM思想vue的.vue文件是怎么run起来的(vue-loader)Vuex和Redux都参照了的Flux模式简单版实现vue中的computed的this指向问题Vue 进阶系列(一)之响应式原理及实现在Vue项目中加载krpano全景图如何用vue封装一个防用户删除的平铺页面的水印组件5个Vuex插件,让你下一个VueJS项目开发速度提升3倍Vue源码中为什么要const _toStr = Object.prototype.toString?初探 Vue3.0 中的一大亮点——Proxy !vue中async-await的使用误区快速利用 vue 或者 react 开发 chrome 插件Vue中的基础过渡动画原理解析浏览器事件循环机制与Vue nextTick的实现Vue 源码(一):响应式原理用 vue + d3 画一棵树为什么我会选择 React + Next.js,而不是 Vue 或 Angular?使用Golang的Gin框架和vue编写web应用Vue项目中使用better-scroll实现一个轮播图小白带你学习Vuex从零实现Vue的Toast插件如何在vue项目中优雅的使用SVG记一次简单的vue组件单元测试你可能需要的一本前端小册:Vue 项目构建与开发入门为什么Proxy可以优化vue的数据监听机制月下载量千万的 npm 包被黑客篡改,Vue 开发者可能正在遭受攻击基于vue-cli理解render函数Vue.js的复用组件开发流程Proxy实现vue MVVM实践Vue调试神器之Vue.js devToolsVue一个案例引发「动画」的使用总结Vue.js 3.0发布更新计划基于Vue组件化的日期联动选择器mpvue 单文件页面配置VueConf 杭州 PPT深入浅出Vue使用中的小技巧手把手教你使用 VuePress 搭建个人博客利用Vue原理实现一个mini版的MVVM框架Vue.js 图标选择组件实践逐行粒度的vuex源码分析Vue一个案例引发「内容分发slot」的最全总结Vue英文推荐列表Stories, Chapters and Paragraphs: Structuring Content with Storyblok and Vue.js – Markus OberlehnerStructuring a Vue project — Authentication – Boris SavicVue Development In 2019: What You Need To Know - Anthony Gore Let’s talk about an unnecessary but popular Vue plugin - heftyheadWorking with the Camera in a NativeScript Vue App – Raymond CamdenGitHub - f/vue-smart-routeGitHub - posva/vue-local-scopePromoted Get all products by Creative Tim including Vue premium dashboards 90% offBest resources to learn Vue.js in 2018The Vue.js Conference in Amsterdam will have everything you hope forOfficial Style Guide for Vue-specific codeLaravel Nova Administration Panel with Vue.jsVuePress: What is it and Why it is a great tool to useVue.js Frameworks & Libraries to use in your next project Integrating content management into your Vue.js projects with PrismicVue.js Amsterdam RecordingsiView UI framework 2.4State of Vue.js 2019 survey7 VueConf Toronto Talks Now Live to WatchVue.JS Components for building Search UIs – All thingsStructuring a Vue project — Authentication – Boris Savic Best Code Editor for Vue.js – Vue Mastery My Favorite Vue.js & Nuxt.js packages for 2019 – Nada Rifki Turn your Vue Web App into a PWA! – Bits and PiecesThe State of Javascript 2018: The View on VueVue 2 + Firebase: How to add Firebase Social Sign In into your Vue application更多推荐查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 ...

December 18, 2018 · 2 min · jiezi

Vue2.0 + ElementUI 手写权限管理系统后台模板(三)——页面搭建

框架布局本章只介绍基础布局,和一些主要的js,页面上基本上都是些交互事件,项目代码上都有注释,不懂的地方debug跑一变就知道了,只是这些事件基本上没有独立存在的,相互之间都有关联框架风格新建页面:/src/views/layout/layout.vue<!– layout.vue –><template> <div id=“loyout”> <el-container> <layoutAside></layoutAside> <el-container> <layoutHeader></layoutHeader> <el-main id=“elmain”> <transition name=“main” mode=“out-in”> <router-view></router-view> </transition> </el-main> <el-footer> <Bottom></Bottom> </el-footer> </el-container> </el-container> </div></template>aside 无限级菜单组件新建页面:/src/views/layout/aside/aside.vue<!– aside.vue –><template> <div> <el-aside id=“asideNav”> <div class=“logo-name”> <p v-if="$store.getters.logoShow">XU</p> <p v-else>vue-xuAdmin后台模板</p> </div> <!– el-menu的属性查看官方文档 –> <el-menu :default-active="$route.path" class=“el-menu-vertical” @select=“selectmenu” :collapse="$store.getters.isCollapse" background-color="#03152A" text-color=“rgba(255,255,255,.7)” active-text-color="#ffffff" :router="$store.getters.uniquerouter" :unique-opened="$store.getters.uniquerouter" :collapse-transition=“true” > <!– 遍历根据权限生成的路由表生成菜单列表 –> <template v-for="(item,index) in $store.getters.routers" v-if="!item.hidden"> <!– 检查是否带有alone属性的一级菜单类似“主页”,还有子菜单的个数 –> <el-submenu v-if="!item.alone && item.children.length>0" :index=“index+’’"> <template slot=“title”> <!– 如果没有设置图标将会采用默认图标‘fa fa-server’ –> <i :class=“item.iconCls?item.iconCls:[fa,fa-server]"></i> <span slot=“title”>{{ $t(routeNmae.${item.name}) }}</span> </template> <!– 子菜单组件 –> <menu-tree :menuData=“item.children”></menu-tree> </el-submenu> <!– 一级菜单 –> <el-menu-item :index=“item.path” v-else> <i :class=“item.iconCls?item.iconCls:[fa,fa-file]"></i> <span slot=“title”>{{ $t(routeNmae.${item.name}) }}</span> </el-menu-item> </template> </el-menu> </el-aside> </div></template>点击菜单// aside.vuewatch: { // 监听浏览器直接输入路由,将此路由添加到tabnavBox ‘$route.path’: function (val) { this.selectmenu(val) } }, // 点击菜单把当前菜单的name和path添加到tabNavBox容器,生成tabNav标签页菜单selectmenu (key) { // 获取当前权限路由表 let router = this.$store.getters.routers let name = ’’ // 查找路由的name属性 let navTitle = function (path, routerARR) { for (let i = 0; i < routerARR.length; i++) { if (routerARR[i].children.length > 0 || routerARR[i].path === path) { if (routerARR[i].path === path && routerARR[i].children.length < 1) { name = routerARR[i].name break } // 递归查找 navTitle(path, routerARR[i].children) } } return name } // tabNavBox添加数据 this.$store.dispatch(‘addTab’, { title: navTitle(key, router), path: key }) }子菜单组件 menu-true新建页面:/src/views/layout/aside/menuTree.vue<!– menuTree.vue –><template> <div> <template v-for="(child,index) in menuData”> <el-submenu v-if=“child.children.length > 0” :index=“child.path”> <template slot=“title”> <i :class=“child.iconCls?child.iconCls:[fa,fa-file]"></i> <span slot=“title”>{{ $t(routeNmae.${child.name}) }}</span> </template> <!– 通过递归 menu-tree 生成无限级菜单 –> <menu-tree :menuData=“child.children”></menu-tree> </el-submenu> <el-menu-item v-else :index=“child.path”> <i :class=“child.iconCls?child.iconCls:[fa,fa-file]"></i> <span slot=“title”>{{ $t(routeNmae.${child.name}) }}</span> </el-menu-item> </template> </div></template>header头部这里没啥好说的,都是html布局,tabnav接下来说, i18n后面会讲新建页面:/src/views/layout/header/header.vue<!– header.vue –><template> <div> <el-header id=“header”> <span class=“hideAside” @click=“collapse”><i class=“fa fa-indent fa-lg”></i></span> <ul class=“personal”> <li class=“fullScreen” @click=“fullScreen”> <el-tooltip class=“item” effect=“dark” content=“全屏” placement=“bottom”><i class=“fa fa-arrows-alt fa-lg”></i></el-tooltip> </li> <li> <langSelect></langSelect> </li> <li>{{ $t(role.${this.$store.getters.info.role}) }}</li> <li> <el-dropdown @command=“handleCommand”> <span class=“el-dropdown-link”> 夏洛克丶旭<i class=“el-icon-arrow-down el-icon–right”></i> </span> <el-dropdown-menu slot=“dropdown”> <el-dropdown-item command=“a”>{{ $t(‘userDropdownMenu.basicInfor’) }}</el-dropdown-item> <el-dropdown-item command=“b”>{{ $t(‘userDropdownMenu.changePassword’) }}</el-dropdown-item> <el-dropdown-item command=“logout” divided>{{ $t(‘userDropdownMenu.logout’) }}</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </li> <li class=“icon”><img :src=“avatar”/></li> </ul> </el-header> <!– tabNav 组件,标签页菜单 –> <tabNav></tabNav> </div></template>tabNav 组件这里的tabNav标签动画和页面的动画是一样的,都是官方的demo稍微改一下,,只不过页面有mode=“out-in"所以动画时间需要快一点新建页面:/src/views/layout/header/tabNav.vue<!– tabNav.vue –><template> <div> <div class=“tabnavBox”> <transition-group name=“list” tag=“ul”> <!– tabnavBox 是存储所有tabNav的数据容器,每次点击左侧菜单就会把数据添加到tabnavBox –> <li v-for="(item, index) in $store.getters.tabnavBox” @contextmenu.prevent=“openMenu(item,$event,index)” :key=“item.title” class=“tabnav” :class=”{ active: $route.path === item.path }"> <router-link :to=“item.path”>{{ $t(routeNmae.${item.title}) }}</router-link> <i @click=“removeTab(item)” class=“el-icon-error” v-if=“index !== 0”></i> </li> </transition-group> </div> <!– 右击菜单 –> <ul v-show=“this.rightMenuShow” :style="{left:this.left+‘px’,top:this.top+‘px’}” class=“menuBox”> <li @click=“removeTab($store.getters.rightNav)"><i class=“fa fa-remove”></i>{{ $t(‘rightMenu.close’) }}</li> <li @click=“removeOtherTab($store.getters.rightNav)">{{ $t(‘rightMenu.closeOther’) }}</li> <li @click=“removeAllTab”>{{ $t(‘rightMenu.closeAll’) }}</li> </ul> </div></template> ...

December 17, 2018 · 2 min · jiezi

webpack打包

webpack打包指令npm run build使用webpack打包vue项目,需要更改配置1、webpack打包后生成dist文件,将dist文件更改成project,放在服务器中,会报错js的引用路径不对,页面空白。解决办法:找到 config > index.jsbuild: { index: path.resolve(__dirname, ‘../dist/index.html’), assetsRoot: path.resolve(__dirname, ‘../dist’), assetsSubDirectory: ‘static’, // assetsPublicPath: ‘/’, // 在/后边+文件名 assetsPublicPath: ‘/project’,2、图片路径不对举例:在src > assets > img > index.jpg 放置图片在src > components > home > Home.vue 引用.home-bg background-image: url("../../assets/img/index.jpg")打包生成文件后,图片加载路径会出问题。解决办法:找到 build > utils.jsif (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: ‘vue-style-loader’, // 添加publicPath: ‘../../’ publicPath: ‘../../’ }) } else { return [‘vue-style-loader’].concat(loaders) }再次打包就可以了

December 13, 2018 · 1 min · jiezi

封装框架的实践

最近在尝试着封装一个框架,碍于种种原因,先从简单的入手吧。基于vue和elementUI封装的框架,集成 数据存储localforage、字体图标库font-awesome、css拓展语言scss、网络请求axios等模块,为了让业务开发更专注于数据驱动。项目源码地址:https://gitee.com/g2333/data_…使用场景1. 环境 框架基于vue2.0开发,故开发环境也需要nodejs和vue-cli。2. 拓展和维护 为使框架本身易拓展和维护,项目采用vue-cli封装,在开发和使用过程都不打包,保持程序的可读性,同时也方便在引用该模块时可简单的修改配置文件和源码。3. 便捷使用 在一个全新的vue-cli初始化项目中, 安装模块(在vue项目路径下npm i modulecomponents), 引用模块(在vue项目的main.js中添加import ‘modulecomponents/index.js’) 测试使用(比如使用框架暴露的方法dataTool.alert(‘测试成功’))项目配置1. 依赖模块 框架本身依赖有如下模块: elementUI 框架的主力,用于组件封装和方法的调用、 localforage 数据存储,用于存储前端的大量数据、 font-awesome 字体图标库、 scss css拓展语言、 axios 网络请求2. 设置项目入口 修改package.json文件,添加main字段,指向项目入口(“main”: “mc/index.js”),修改private字段,设置为开源(“private”: false)3. 项目初始化 为了让框架方便引用,故在初始化文件index.js(框架项目开发过程使用indexdsForDev.js),自动引入依赖和全局变量的挂载4. 文件提交 设置项目.gitignore文件忽略node_modules避免在协同开发时因为环境不一致导致的webpack报错 设置项目.npmignore文件忽略发布时非必要的文件,减少模块的体积封装的模块1. 组件 组件基于elementUI封装,项目中封装的组件为避免命名冲突,都以mc-为前缀开头。 计划封装的组件有如下: 表格mc-table、 表单mc-form、 树列表mc-tree、 对话框mc-dialog、 上下文菜单mc-contentmenu、 按钮组mc-btns、 流图mc-flow、 下拉选框mc-select、 附件上传mc-upload//在界面上显示一个表单<mc-form :object=“form”></mc-form>//表单对象,描述表单的结构和数据form: new mc.Form({ structure: [{ label: ‘测试’, name: ’test’, }], data: { test: ‘hello world’, }}) 除框架封装的组件外,依旧支持使用elementUI组件2. 全局方法 为了方便开发,较为常用的方法被挂载在全局变量dataTool的属性中,比如 请求方法:ajax请求httpReq、文件导出exportFile、文件上传uploadFile; 提示类方法:警告弹框alert、边角提示notify、确认输入框confirm、锁屏加载loading等; 调用组件类方法:打开弹窗openDialog、关闭弹窗closeDialog、打开上下文菜单openContextmenu、关闭上下文菜单closeContextmenu等; 数据处理:对象类型的克隆和过滤objClone、时间格式的转化formatTime、cookie的添加setCookie等; 原型链上的方法:获取表格新增的一行数据Array.newTableRow、数组元素位置交换Array.swap等; 事件方法:注册事件addEvent、触发事件emitEvent、取消事件cancelEvent等;//打开上下文菜单,点击导出文件,将请求的内容导出成flow.json文件dataTool.openContextmenu(event,[{ text: ‘导出文件’, icon: ‘fa fa-download’, color: ‘blue’, click: ()=>{ const reqObj = {url:‘http://rap2api.taobao.org/app/mock/22119/FUNC=getFlow’, params: {}, type:‘mock’}; dataTool.httpReq(reqObj).then(res=>{ dataTool.exportFile({fileName: ‘flow.json’,data: JSON.stringify(res.CONTENT)}); }); }}])3. 配置文件 封装的组件各有一份默认配置文件,方便全局调整组件的参数。 封装的组件既支持组件类的默认参数修改,也兼容修改单个实例和继承组件类export default { //表单类的配置文件 btns: [], //表单底部栏按钮 topBtns: [], //表单顶部栏按钮 hiddenRows: [], //隐藏的行 topBtnStyle: ‘’, bottomBtnStyle: ’text-align:right’, dialogEdit: false, //是否开启普通字符串类型的弹窗编辑功能 showRules: true, //是否显示表单规则验证 style: “margin: 10px;”, inline: false, labelWidth: “50px”, labelPosition: “right”, size: “small”, autoComplete: ‘on’, spellcheck: false, readOnly: false, extBtnIcon: ’el-icon-more’, textArea: { size: { minRows: 1, maxRows: 10}, resize: ’none’, }, tag: { input: ‘’, type: ‘warning’, closeTransition: false, appendWord: ’ + New Tag’, }, inputStyle: ‘width:100%’, dataType: { //采用小写,减少枚举数量 bool: [‘bool’,‘boolean’,‘switch’], checkboxGroup: [‘checkboxgroup’,‘checkbox’], radio: [‘radio’], select: [‘singleenum’,‘multiselect’,‘multienum’], time: [’time’], date: [‘date’,‘datetime’,‘datetimerange’,‘daterange’], button: [‘button’,‘btn’], tag: [’tags’,’tag’], input: [’’,‘input’,‘string’,’text’,’textarea’,’number’,‘float’,‘password’,‘double’,‘int’,‘integer’,’long’,‘search’,’extinput’], component: [‘mc-table’], },}开发记录1. 项目结构 整体项目的规划整理在一个xmind文件中,方便记录开发进度和了解项目的整体大纲,这是图片版 http://qpic.cn/dDPbFwEeD (请在复制粘贴到浏览器的地址栏中访问)2. 使用文档 为了记录开发进度和形成规范,项目开发的使用说明和修改会记录在石墨文档https://shimo.im/sheet/K8QPjP…3. 版本控制 使用git作为版本控制,项目的源码托管在码云上https://gitee.com/g2333/data_… 既方便协同开发,也方便代码版本控制框架更新1. 项目更新 修改后的源码在测试成功后,修改package.json中的版本号,将代码推送到码云上,然后通过npm发布新版本2. 模块更新 通过npm update modulecomponents指令更新模块,即可使用最新版功能 ...

December 9, 2018 · 1 min · jiezi

vue-cli在webpack的配置文件探究

我们在构建一个vue项目的时候是不是一顿操作猛如虎啊?npm install vue-cli -gvue init webpack vue-demonpm installnpm run dev然后稍等一小会儿,咱们就能在浏览器看到了咱们的界面啦实际操作项目里面咱们可能需要修改一些配置项才能满足咱们自己项目的需求先看看vue项目初始化的文件目录结构:├─build ├─config ├─dist├─node_modules├─src│ ├─assets│ ├─components│ ├─router│ ├─App.vue│ ├─main.js├─static├─.babelrc├─.editorconfig├─.gitignore├─.postcssrc.js├─index.html├─package-lock.json├─package.json└─README.md来看看重要的文件,按我理解的顺序来啊:1.package.json项目作为一个大家庭,每个文件都各司其职。package.json来制定名单,需要哪些npm包来参与到项目中来,npm install命令根据这个配置文件增减来管理本地的安装包。这里面存放着我们项目的配置项,比如引入的依赖、项目作者、项目版本、项目编译启动的命令(放到script下面的)、还有就是dependencies和devDependencies、node和npm的信息、浏览器版本信息等等。这儿说一下这个dependencies和devDependencies我们在使用npm install 安装模块或插件的时候,有两种命令把他们写入到 package.json 文件里面去,比如:–save-dev 安装的 插件,被写入到 devDependencies 对象里面去–save 安装的 插件 ,被写入到 dependencies 对象里面去package.json 文件里面的 devDependencies 和 dependencies 对象有什么区别呢?devDependencies 里面的插件只用于开发环境,不用于生产环境dependencies 是需要发布到生产环境的。npm install 会安装两个依赖npm install packagename 只安装dependencies的依赖npm install packagename –dev 会安装devDependencies的依赖如果你只是单纯的使用这个包而不需要进行一些改动测试之类的,只安装dependencies而不安装devDependencies。执行:npm install –productionfile-loader和url-loader的区别:以图片为例,file-loader可对图片进行压缩,但是还是通过文件路径进行引入,当http请求增多时会降低页面性能,而url-loader通过设定limit参数,小于limit字节的图片会被转成base64的文件,大于limit字节的将进行图片压缩的操作。总而言之,url-loader是file-loader的上层封装。2、.postcssrc.js.postcssrc.js文件其实是postcss-loader包的一个配置,在webpack的旧版本可以直接在webpack.config.js中配置,现版本中postcss的文档示例独立出.postcssrc.js,里面写进去需要使用到的插件。module.exports = { “plugins”: { “postcss-import”: {}, “postcss-url”: {}, “autoprefixer”: {} }}关于这个postcss,去搜一下吧,这玩意儿我估摸着以后肯定会成为继scss和less后最火的一个css预处理咳咳,觉得自己英语能过关的去这儿看看 postcss咱们这还是有中文的,哈哈 postcss3、 .babelrc要了解这玩意儿,就内容超级多了,babel在vue项目里面就是为了把ES6的语法转换成ES5,废话不多说,咱们还是给链接把,笑哭了 babel官方网站 去这儿直接了解这个神奇的东东4、src内文件我们开发的代码都存放在src目录下,根据需要我们通常会再建一些文件夹。比如pages的文件夹,用来存放页面让components文件夹专门做好组件的工作;api文件夹,来封装请求的参数和方法;store文件夹,使用vuex来作为vue的状态管理工具,我也常叫它作前端的数据库等。assets文件:脚手架自动回放入一个图片在里面作为初始页面的logo。平常我们使用的时候会在里面建立js,css,img,fonts等文件夹,作为静态资源调用components文件夹:用来存放组件,合理地使用组件可以高效地实现复用等功能,从而更好地开发项目。一般情况下比如创建头部组件的时候,我们会新建一个header的文件夹,然后再新建一个header.vue的文件夹router文件夹:该文件夹下有一个叫index.js文件,用于实现页面的路由跳转,具体使用请点击→vue-router传送门App.vue:作为我们的主组件,可通过使用开放入口让其他的页面组件得以显示。main.js:作为我们的入口文件,主要作用是初始化vue实例并使用需要的插件,小型项目省略router时可放在该处5、其他文件.editorconfig:编辑器的配置文件.gitignore:忽略git提交的一个文件,配置之后提交时将不会加载忽略的文件index.html:页面入口,经过编译之后的代码将插入到这来。package.lock.json:锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致README.md:可此填写项目介绍node_modules:根据package.json安装时候生成的的依赖(安装包).eslint相关文件,存放项目的编写格式规范,例如缩进,末尾分号,单双引号,对项目开发很有帮助,特别是大项目,规范所有人的开发,减少代码规范冲突config文件夹├─config │ ├─dev.env.js │ ├─index.js │ ├─prod.env.js1、config/dev.env.jsconfig内的文件其实是服务于build的,大部分是定义一个变量export出去’use strict’//采用严格模式const merge = require(‘webpack-merge’)const prodEnv = require(’./prod.env’)//webpack-merge提供了一个合并函数,它将数组和合并对象创建一个新对象。//如果遇到函数,它将执行它们,通过算法运行结果,然后再次将返回的值封装在函数中.这边将dev和prod进行合并module.exports = merge(prodEnv, { NODE_ENV: ‘“development”’})2、config/prod.env.js当开发是调取dev.env.js的开发环境配置,发布时调用prod.env.js的生产环境配置’use strict’module.exports = { NODE_ENV: ‘“production”’}3、config/index.js’use strict’const path = require(‘path’)module.exports = { dev: { // 开发环境下面的配置 assetsSubDirectory: ‘static’, //子目录,一般存放css,js,image等文件 assetsPublicPath: ‘/’, //根目录 proxyTable: {}, //可利用该属性解决跨域的问题(axios) host: ’localhost’, // 地址 port: 8080, //端口号设置,端口号占用出现问题可在此处修改 autoOpenBrowser: false, //是否在编译(输入命令行npm run dev)后打开http://localhost:8080/页面,以前配置为true,近些版本改为false,个人偏向习惯自动打开页面 errorOverlay: true, //浏览器错误提示 notifyOnErrors: true, //跨平台错误提示 poll: false, //使用文件系统(file system)获取文件改动的通知devServer.watchOptions devtool: ‘cheap-module-eval-source-map’,//增加调试,该属性为原始源代码(仅限行)不可在生产环境中使用 cacheBusting: true, //使缓存失效 cssSourceMap: true //代码压缩后进行调bug定位将非常困难,于是引入sourcemap记录压缩前后的位置信息记录,当产生错误时直接定位到未压缩前的位置,将大大的方便我们调试 }, build: { // 生产环境下面的配置 index: path.resolve(__dirname, ‘../dist/index.html’), //index编译后生成的位置和名字,根据需要改变后缀,比如index.php assetsRoot: path.resolve(__dirname, ‘../dist’), //编译后存放生成环境代码的位置 assetsSubDirectory: ‘static’, //js,css,images存放文件夹名 assetsPublicPath: ‘/’, //发布的根目录,通常本地打包dist后打开文件会报错,此处修改为./。如果是上线的文件,可根据文件存放位置进行更改路径 productionSourceMap: true, devtool: ‘#source-map’, productionGzip: false, //unit的gzip命令用来压缩文件,gzip模式下需要压缩的文件的扩展名有js和css productionGzipExtensions: [‘js’, ‘css’], bundleAnalyzerReport: process.env.npm_config_report }}build文件夹├─build │ ├─build.js │ ├─check-versions.js │ ├─utils.js │ ├─vue-loader.conf.js │ ├─webpack.base.conf.js │ ├─webpack.dev.conf.js │ ├─webpack.prod.conf.js 1、build/build.js’use strict’require(’./check-versions’)() //check-versions:调用检查版本的文件。加()代表直接调用该函数process.env.NODE_ENV = ‘production’ //设置当前是生产环境//下面定义常量引入插件const ora = require(‘ora’) //加载动画const rm = require(‘rimraf’) //删除文件const path = require(‘path’)const chalk = require(‘chalk’) //对文案输出的一个彩色设置const webpack = require(‘webpack’)const config = require(’../config’) //默认读取下面的index.js文件const webpackConfig = require(’./webpack.prod.conf’)//调用start的方法实现加载动画,优化用户体验const spinner = ora(‘building for production…’)spinner.start()//先删除dist文件再生成新文件,因为有时候会使用hash来命名,删除整个文件可避免冗余rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, // 如果您使用的是ts-loader,将其设置为true将导致在构建期间出现TypeScript错误 chunks: false, chunkModules: false }) + ‘\n\n’) if (stats.hasErrors()) { console.log(chalk.red(‘构建失败并出现错误\n’)) process.exit(1) } console.log(chalk.cyan(‘构建成功.\n’)) console.log(chalk.yellow( ‘Tip: 构建的文件旨在通过HTTP服务器提供.\n’ + ‘正在打开index.html 在file:// won't work.\n’ )) })})2、build/check-version.js该文件用于检测node和npm的版本,实现版本依赖’use strict’const chalk = require(‘chalk’)const semver = require(‘semver’) //对版本进行检查const packageConfig = require(’../package.json’)const shell = require(‘shelljs’)function exec (cmd) { //返回通过child_process模块的新建子进程,执行 Unix 系统命令后转成没有空格的字符串 return require(‘child_process’).execSync(cmd).toString().trim()}const versionRequirements = [ { name: ’node’, currentVersion: semver.clean(process.version), //使用semver格式化版本 versionRequirement: packageConfig.engines.node //获取package.json中设置的node版本 }]if (shell.which(’npm’)) { versionRequirements.push({ name: ’npm’, currentVersion: exec(’npm –version’), // 自动调用npm –version命令,并且把参数返回给exec函数,从而获取纯净的版本号 versionRequirement: packageConfig.engines.npm })}module.exports = function () { const warnings = [] for (let i = 0; i < versionRequirements.length; i++) { const mod = versionRequirements[i] if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { //上面这个判断就是如果版本号不符合package.json文件中指定的版本号,就执行下面错误提示的代码 warnings.push(mod.name + ‘: ’ + chalk.red(mod.currentVersion) + ’ should be ’ + chalk.green(mod.versionRequirement) ) } } if (warnings.length) { console.log(’’) console.log(chalk.yellow(‘To use this template, you must update following to modules:’)) console.log() for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.log(’ ’ + warning) } console.log() process.exit(1) }}3、build/utils.jsutils是工具的意思,是一个用来处理css的文件。‘use strict’const path = require(‘path’)const config = require(’../config’)const ExtractTextPlugin = require(’extract-text-webpack-plugin’)const packageConfig = require(’../package.json’)//导出文件的位置,根据环境判断开发环境和生产环境,为config文件中index.js文件中定义的build.assetsSubDirectory或dev.assetsSubDirectoryexports.assetsPath = function (_path) { const assetsSubDirectory = process.env.NODE_ENV === ‘production’ ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory //Node.js path 模块提供了一些用于处理文件路径的小工具 return path.posix.join(assetsSubDirectory, _path)}exports.cssLoaders = function (options) { options = options || {} //使用了css-loader和postcssLoader,通过options.usePostCSS属性来判断是否使用postcssLoader中压缩等方法 const cssLoader = { loader: ‘css-loader’, options: { sourceMap: options.sourceMap } } const postcssLoader = { loader: ‘postcss-loader’, options: { sourceMap: options.sourceMap } } // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] if (loader) { loaders.push({ loader: loader + ‘-loader’, //Object.assign是es6语法的浅复制,后两者合并后复制完成赋值 options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } //ExtractTextPlugin可提取出文本,代表首先使用上面处理的loaders,当未能正确引入时使用vue-style-loader if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: ‘vue-style-loader’ }) } else { //返回vue-style-loader连接loaders的最终值 return [‘vue-style-loader’].concat(loaders) } } // https://vue-loader.vuejs.org/en/configurations/extract-css.html return { css: generateLoaders(), //需要css-loader 和 vue-style-loader postcss: generateLoaders(), //需要css-loader和postcssLoader 和 vue-style-loader less: generateLoaders(’less’), //需要less-loader 和 vue-style-loader sass: generateLoaders(‘sass’, { indentedSyntax: true }), //需要sass-loader 和 vue-style-loader scss: generateLoaders(‘sass’), //需要sass-loader 和 vue-style-loader stylus: generateLoaders(‘stylus’), //需要stylus-loader 和 vue-style-loader styl: generateLoaders(‘stylus’) //需要stylus-loader 和 vue-style-loader }}// Generate loaders for standalone style files (outside of .vue)exports.styleLoaders = function (options) { const output = [] const loaders = exports.cssLoaders(options) //将各种css,less,sass等综合在一起得出结果输出output for (const extension in loaders) { const loader = loaders[extension] output.push({ test: new RegExp(’\.’ + extension + ‘$’), use: loader }) } return output}exports.createNotifierCallback = () => { //发送跨平台通知系统 const notifier = require(’node-notifier’) return (severity, errors) => { if (severity !== ’error’) return //当报错时输出错误信息的标题,错误信息详情,副标题以及图标 const error = errors[0] const filename = error.file && error.file.split(’!’).pop() notifier.notify({ title: packageConfig.name, message: severity + ‘: ’ + error.name, subtitle: filename || ‘’, icon: path.join(__dirname, ’logo.png’) }) }}path.posix:提供对路径方法的POSIX(可移植性操作系统接口)特定实现的访问,即可跨平台,区别于win32。path.join:用于连接路径,会正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是"“4、vue-loader.conf.js该文件的主要作用就是处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。‘use strict’const utils = require(’./utils’)const config = require(’../config’)const isProduction = process.env.NODE_ENV === ‘production’const sourceMapEnabled = isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap//处理项目中的css文件,生产环境和测试环境默认是打开sourceMap,而extract中的提取样式到单独文件只有在生产环境中才需要module.exports = { loaders: utils.cssLoaders({ sourceMap: sourceMapEnabled, extract: isProduction }), cssSourceMap: sourceMapEnabled, cacheBusting: config.dev.cacheBusting, // 在模版编译过程中,编译器可以将某些属性,如 src 路径,转换为require调用,以便目标资源可以由 webpack 处理. transformToRequire: { video: [‘src’, ‘poster’], source: ‘src’, img: ‘src’, image: ‘xlink:href’ }}5、webpack.base.conf.jswebpack.base.conf.js是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等。‘use strict’const path = require(‘path’)const utils = require(’./utils’)const config = require(’../config’)const vueLoaderConfig = require(’./vue-loader.conf’)function resolve (dir) {//拼接出绝对路径 return path.join(__dirname, ‘..’, dir)}// 添加eslint过滤const createLintingRule = () => ({ test: /.(js|vue)$/, loader: ’eslint-loader’, enforce: ‘pre’, include: [resolve(‘src’), resolve(’test’)], options: { formatter: require(’eslint-friendly-formatter’), emitWarning: !config.dev.showEslintErrorsInOverlay }})module.exports = { //path.join将路径片段进行拼接,而path.resolve将以/开始的路径片段作为根目录,在此之前的路径将会被丢弃 //path.join(’/a’, ‘/b’) // ‘a/b’,path.resolve(’/a’, ‘/b’) // ‘/b’ context: path.resolve(__dirname, ‘../’), //配置入口,默认为单页面所以只有app一个入口 entry: { app: ‘./src/main.js’ }, //配置出口,默认是/dist作为目标文件夹的路径 output: { path: config.build.assetsRoot, filename: ‘[name].js’, publicPath: process.env.NODE_ENV === ‘production’ ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { //自动的扩展后缀,比如一个js文件,则引用时书写可不要写.js extensions: [’.js’, ‘.vue’, ‘.json’], //创建路径的别名,比如增加’components’: resolve(‘src/components’)等 alias: { ‘vue$’: ‘vue/dist/vue.esm.js’, ‘@’: resolve(‘src’), } }, //使用插件配置相应文件的处理方法 module: { rules: [ …(config.dev.useEslint ? [createLintingRule()] : []), // 加载eslint代码规范规则 //使用vue-loader将vue文件转化成js的模块 { test: /.vue$/, loader: ‘vue-loader’, options: vueLoaderConfig }, //js文件需要通过babel-loader进行编译成es5文件以及压缩等操作 { test: /.js$/, loader: ‘babel-loader’, include: [resolve(‘src’), resolve(’test’), resolve(’node_modules/webpack-dev-server/client’)] }, //图片、音像、字体都使用url-loader进行处理,超过10000会编译成base64 { test: /.(png|jpe?g|gif|svg)(?.)?$/, loader: ‘url-loader’, options: { limit: 10000, name: utils.assetsPath(‘img/[name].[hash:7].[ext]’) } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.)?$/, loader: ‘url-loader’, options: { limit: 10000, name: utils.assetsPath(‘media/[name].[hash:7].[ext]’) } }, { test: /.(woff2?|eot|ttf|otf)(?.)?$/, loader: ‘url-loader’, options: { limit: 10000, name: utils.assetsPath(‘fonts/[name].[hash:7].[ext]’) } } ] },//以下选项是Node.js全局变量或模块,这里主要是防止webpack注入一些Node.js的东西到vue中 node: { // 防止webpack注入无用的setImmediate polyfill,因为Vue源包含它(虽然只有它是原生的才使用它) setImmediate: false, // 防止webpack将模拟注入到对客户端没有意义的Node本机模块 dgram: ’empty’, fs: ’empty’, net: ’empty’, tls: ’empty’, child_process: ’empty’ }}6、webpack.dev.conf.js’use strict’const utils = require(’./utils’)const webpack = require(‘webpack’)const config = require(’../config’)//通过webpack-merge实现webpack.dev.conf.js对wepack.base.config.js的继承const merge = require(‘webpack-merge’)const path = require(‘path’)const baseWebpackConfig = require(’./webpack.base.conf’)const CopyWebpackPlugin = require(‘copy-webpack-plugin’)const HtmlWebpackPlugin = require(‘html-webpack-plugin’)//美化webpack的错误信息和日志的插件const FriendlyErrorsPlugin = require(‘friendly-errors-webpack-plugin’)// 查看空闲端口位置,默认情况下搜索8000这个端口const portfinder = require(‘portfinder’)// processs为node的一个全局对象获取当前程序的环境变量,即hostconst HOST = process.env.HOSTconst PORT = process.env.PORT && Number(process.env.PORT)const devWebpackConfig = merge(baseWebpackConfig, { module: { //规则是工具utils中处理出来的styleLoaders,生成了css,less,postcss等规则 rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) }, //增强调试 devtool: config.dev.devtool, //此处的配置都是在config的index.js中设定好了 devServer: { clientLogLevel: ‘warning’, //控制台显示的选项有none, error, warning 或者 info //当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html historyApiFallback: { rewrites: [ { from: /./, to: path.posix.join(config.dev.assetsPublicPath, ‘index.html’) }, ], }, hot: true, //热加载 contentBase: false, // since we use CopyWebpackPlugin. compress: true, //压缩 host: HOST || config.dev.host, port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, //调试时自动打开浏览器 overlay: config.dev.errorOverlay ? { warnings: false, errors: true } : false, // warning 和 error 都要显示 publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxyTable, quiet: true, //控制台是否禁止打印警告和错误,若用FriendlyErrorsPlugin 此处为 true watchOptions: { poll: config.dev.poll, // 文件系统检测改动 } }, plugins: [ new webpack.DefinePlugin({ ‘process.env’: require(’../config/dev.env’) }), new webpack.HotModuleReplacementPlugin(), //模块热替换插件,修改模块时不需要刷新页面 new webpack.NamedModulesPlugin(), // 显示文件的正确名字 new webpack.NoEmitOnErrorsPlugin(), //当webpack编译错误的时候,来中端打包进程,防止错误代码打包到文件中 // https://github.com/ampedandwired/html-webpack-plugin // 该插件可自动生成一个 html5 文件或使用模板文件将编译好的代码注入进去 new HtmlWebpackPlugin({ filename: ‘index.html’, template: ‘index.html’, inject: true }), //复制插件 new CopyWebpackPlugin([ { from: path.resolve(__dirname, ‘../static’), to: config.dev.assetsSubDirectory, ignore: [’.*’] //忽略.的文件 } ]) ]})module.exports = new Promise((resolve, reject) => { portfinder.basePort = process.env.PORT || config.dev.port //查找端口号 portfinder.getPort((err, port) => { if (err) { reject(err) } else { //端口被占用时就重新设置evn和devServer的端口 process.env.PORT = port devWebpackConfig.devServer.port = port //友好地输出错误信息 devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ compilationSuccessInfo: { messages: [Your application is running here: http://${devWebpackConfig.devServer.host}:${port}], }, onErrors: config.dev.notifyOnErrors ? utils.createNotifierCallback() : undefined })) resolve(devWebpackConfig) } })})7、webpack.prod.conf.js’use strict’const path = require(‘path’)const utils = require(’./utils’)const webpack = require(‘webpack’)const config = require(’../config’)const merge = require(‘webpack-merge’)const baseWebpackConfig = require(’./webpack.base.conf’)const CopyWebpackPlugin = require(‘copy-webpack-plugin’)const HtmlWebpackPlugin = require(‘html-webpack-plugin’)const ExtractTextPlugin = require(’extract-text-webpack-plugin’)const OptimizeCSSPlugin = require(‘optimize-css-assets-webpack-plugin’)const UglifyJsPlugin = require(‘uglifyjs-webpack-plugin’)const env = require(’../config/prod.env’)const webpackConfig = merge(baseWebpackConfig, { module: { //调用utils.styleLoaders的方法 rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, //开启调试的模式。默认为true extract: true, usePostCSS: true }) }, devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath(‘js/[name].[chunkhash].js’), chunkFilename: utils.assetsPath(‘js/[id].[chunkhash].js’) }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ ‘process.env’: env }), new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false //警告:true保留警告,false不保留 } }, sourceMap: config.build.productionSourceMap, parallel: true }), //抽取文本。比如打包之后的index页面有style插入,就是这个插件抽取出来的,减少请求 new ExtractTextPlugin({ filename: utils.assetsPath(‘css/[name].[contenthash].css’), // 将以下选项设置为“false”将不会从codesplit块中提取CSS // 当webpack加载了codesplit块时,他们的CSS将使用style-loader动态插入 // 它目前设置为“true”,因为我们看到源代码包含在codesplit包中,当它是“false”增加文件大小时:https://github.com/vuejs-templates/webpack/issues/1110 allChunks: true, }), // 压缩提取的CSS。 我们正在使用此插件,以便可以减少来自不同组件的可能重复的CSS new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }), // 使用正确的资产哈希生成dist index.html以进行缓存. // 您可以通过编辑/index.html来自定义输出 // 请参阅https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: config.build.index, template: ‘index.html’, inject: true, minify: { removeComments: true, //删除注释 collapseWhitespace: true, //删除空格 removeAttributeQuotes: true //删除属性的引号 // 更多属性参见:https://github.com/kangax/html-minifier#options-quick-reference }, // 通过CommonsChunkPlugin持续使用多个块的必要条件 chunksSortMode: ‘dependency’ //模块排序,按照我们需要的顺序排序 }), // 当供应商模块没有改变时,保持module.id稳定 new webpack.HashedModuleIdsPlugin(), // 启用域限制 new webpack.optimize.ModuleConcatenationPlugin(), // 将供应商js拆分为自己的文件 new webpack.optimize.CommonsChunkPlugin({ name: ‘vendor’, minChunks (module) { // node_modules中的任何必需模块都将解压缩到供应商 return ( module.resource && /.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, ‘../node_modules’) ) === 0 ) } }), // 将webpack运行时和模块清单提取到其自己的文件中,以防止在更新应用程序包时更新供应商哈希 new webpack.optimize.CommonsChunkPlugin({ name: ‘manifest’, minChunks: Infinity }), // 此实例从代码拆分块中提取共享块,并将它们捆绑在一个单独的块中,类似于供应商块,请参阅:https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk new webpack.optimize.CommonsChunkPlugin({ name: ‘app’, async: ‘vendor-async’, children: true, minChunks: 3 }), // 复制自定义静态资源到dist里面 new CopyWebpackPlugin([ { from: path.resolve(__dirname, ‘../static’), to: config.build.assetsSubDirectory, ignore: [’.’] } ]) ]})if (config.build.productionGzip) { const CompressionWebpackPlugin = require(‘compression-webpack-plugin’) webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: ‘[path].gz[query]’, algorithm: ‘gzip’, test: new RegExp( ‘\.(’ + config.build.productionGzipExtensions.join(’|’) + ‘)$’ ), threshold: 10240, minRatio: 0.8 }) )}if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin())}module.exports = webpackConfig全是代码,代码全是项目模块的东西。估计有好多小伙伴儿看一点点就关了,能看完的,给你比心哦,哈哈哈可以参考那本书叫《深入浅出webpack》 ...

November 23, 2018 · 7 min · jiezi

vue面试题总结

写在前面参考答案在看云平台发布,如果大家想阅读参考答案,可直接购买,看云平台 50 个免费开通权限 已经使用完毕,感谢大家的支持!有什么意见与建议欢迎您及时联系作者或留言回复!Vue开发交流微信群:(二维码定期更新,长期有效!或添加群主微信hanxuming888进群)本文档基于vue-cli技术栈总结了vue-cli工程vue.js核心知识vue-router路由vuex状态管理器axios等http请求移动端适配Tab切换等常用功能vue与原生app混合交互vue生产环境部署vue各项技术源码解析MVVM设计模式vue-cli工程深入拓展等12个关于vue-cli开发的方面,共98道面试题。不仅可以帮你一次性详细阅读所有关于vue的面试题、更可以帮你拓展关于vue开发的视野。关于vue的试题,看这一篇文档就够了!vue-cli工程1、构建的 vue-cli 工程都到了哪些技术,它们的作用分别是什么?2、vue-cli 工程常用的 npm 命令有哪些?3、请说出vue-cli工程中每个文件夹和文件的用处4、config文件夹 下 index.js 的对于工程 开发环境 和 生产环境 的配置5、请你详细介绍一些 package.json 里面的配置参考答案: https://www.kancloud.cn/hanxu...vue核心知识点1、对于Vue是一套渐进式框架的理解2、vue.js的两个核心是什么?3、请问 v-if 和 v-show 有什么区别4、vue常用的修饰符5、v-on可以监听多个方法吗?6、vue中 key 值的作用7、vue-cli工程升级vue版本8、vue事件中如何使用event对象?9、$nextTick的使用10、Vue 组件中 data 为什么必须是函数11、v-for 与 v-if 的优先级12、vue中子组件调用父组件的方法13、vue中 keep-alive 组件的作用14、vue中如何编写可复用的组件?15、什么是vue生命周期和生命周期钩子函数?16、vue生命周期钩子函数有哪些?17、vue如何监听键盘事件中的按键?18、vue更新数组时触发视图更新的方法19、vue中对象更改检测的注意事项20、解决非工程化项目初始化页面闪动问题21、v-for产生的列表,实现active的切换22、v-model语法糖的组件中的使用23、十个常用的自定义过滤器24、vue等单页面应用及其优缺点25、什么是vue的计算属性?26、vue-cli提供的几种脚手架模板27、vue父组件如何向子组件中传递数据?28、vue-cli开发环境使用全局常量29、vue-cli生产环境使用全局常量30、vue弹窗后如何禁止滚动条滚动?31、计算属性的缓存和方法调用的区别32、vue-cli中自定义指令的使用参考答案: https://www.kancloud.cn/hanxu...vue-router1、vue-router如何响应 路由参数 的变化?2、完整的 vue-router 导航解析流程3、vue-router有哪几种导航钩子( 导航守卫 )?4、vue-router的几种实例方法以及参数传递5、vue-router的动态路由匹配以及使用6、vue-router如何定义嵌套路由?7、<router-link></router-link>组件及其属性8、vue-router实现路由懒加载( 动态加载路由 )9、vue-router路由的两种模式10、history路由模式与后台的配合参考答案: https://www.kancloud.cn/hanxu...vuex1、什么是vuex?2、使用vuex的核心概念3、vuex在vue-cli中的应用4、组件中使用 vuex 的值和修改值的地方?5、在vuex中使用异步修改6、pc端页面刷新时实现vuex缓存参考答案: https://www.kancloud.cn/hanxu...http请求1、Promise对象是什么?2、axios、fetch与ajax有什么区别?3、什么是JS的同源策略和跨域问题?4、如何解决跨域问题?5、vue-cli中如何使用JSON数据模拟?6、vue-cli中http请求的统一管理。7、axios有什么特点?参考答案: https://www.kancloud.cn/hanxu...UI样式1、.vue组件的scoped属性的作用2、如何让CSS只在当前组件中起作用?3、vue-cli中常用的UI组件库4、如何适配移动端?【 经典 】5、移动端常用媒体查询的使用6、垂直居中对齐7、vue-cli中如何使用背景图片?8、使用表单禁用时移动端样式问题9、多种类型文本超出隐藏问题参考答案: https://www.kancloud.cn/hanxu…常用功能1、vue中如何实现tab切换功能?2、vue中如何利用 keep-alive 标签实现某个组件缓存功能?3、vue中实现切换页面时为左滑出效果4、vue中父子组件如何相互调用方法?5、vue中央事件总线的使用参考答案: https://www.kancloud.cn/hanxu…混合开发1、vue如何调用 原生app 提供的方法?2、原生app 调用 vue 提供的方法,并将值传递到 .vue 组件中参考答案: https://www.kancloud.cn/hanxu…生产环境1、vue打包命令是什么?2、vue打包后会生成哪些文件?3、如何配置 vue 打包生成文件的路径?4、vue如何优化首屏加载速度?参考答案: https://www.kancloud.cn/hanxu...MVVM设计模式1、MVC、MVP与MVVM模式2、MVC、MVP与MVVM的区别3、常见的实现MVVM几种方式4、Object.defineProperty()方法5、实现一个自己的MVVM(原理剖析)6、 ES6中类和定义7、JS中的文档碎片8、解构赋值9、Array.from与Array.reduce10、递归的使用11、Obj.keys()与Obj.defineProperty12、发布-订阅模式13、实现MVVM的思路分析参考答案: https://www.kancloud.cn/hanxu…源码剖析1、vue内部与运行机制:Vue.js 全局运行机制响应式系统的基本原理什么是 Virtual DOM?如何编译template 模板?diff算法批量异步更新策略及 nextTick 原理?proxy代理?2、vuex工作原理详解Vue.mixinVue.use参考答案: https://www.kancloud.cn/hanxu…深入拓展1、vue开发命令 npm run dev 输入后的执行过程2、vue的服务器端渲染3、从零写一个npm安装包4、vue-cli中常用到的加载器5、webpack的特点参考答案: https://www.kancloud.cn/hanxu…

November 22, 2018 · 1 min · jiezi

一份超级详细的Vue-cli3.0使用教程[赶紧来试试!]

前言在vue-cli 2.X的时候,也写过一篇类似的文章,在八月份的时候vue-cli已经更新到了3.X,新版本的脚手架,功能灰常强大,试用过后非常喜欢,写篇教程来帮助各位踩一下坑。游泳、健身了解一下:博客、前端积累文档、公众号、GitHub主要内容:零配置启动/打包一个.vue文件详细的搭建过程重点推荐:使用图形化界面创建/管理/运行项目安装:卸载旧版本:如果你事先已经全局安装了旧版本的vue-cli(1.x 或 2.x),你需要先卸载它:npm uninstall vue-cli -gNode版本要求:3.x需要在Node.js8.9或更高版本(推荐8.11.0+),点击这里可以安装node大多数人都安装过了node,使用下面的命令行查询你的node版本:node -v如果你的版本不够,可以使用下面的命令行来把Node版本更新到最新的稳定版:npm install -g n // 安装模块 这个模块是专门用来管理node.js版本的n stable // 更新你的node版本mac下,更新版本的时候,如果提示你权限不够:sudo n stable // 我就遇到了安装vue-cli:npm install -g @vue/cli // 安装cli3.xvue –version // 查询版本是否为3.x如果cli3.x用的不舒服,cli3也能使用2.x模板:npm install -g @vue/cli-init // 安装这个模块// 就可以使用2.x的模板:vue init webpack my-project零配置启动/打包一个.vue文件:安装扩展:npm install -g @vue/cli-service-global安装完扩展之后,可以随便找个文件夹建一个如下方示例的.vue文件,然后跑起来:vue serve App.vue // 启动服务vue build App.vue // 打包出生产环境的包并用来部署如下图,只需一个.vue文件,就能迅速启动一个服务:如图所示,服务启动的时候回生成一个node_modules包,稍微测试了一下,服务支持ES6语法和热更新,打包的时候会生成一个dist文件夹。(新建一个test.vue文件也只有一个node_modules/dist文件夹)这是个很棒的功能,用于开发一个库、组件,做一些小demo等都是非常适合的!第一次创建项目:1. 命令行:vue create hello-cli3 hello-cli3是文件夹名字,如果不存在会自动创建文件夹,如果存在会安装到那个文件夹中。相比2.x的时候需要自己手动创建一个文件夹,这里也算是一个小优化吧。2. 选择模板:一开始只有两个选项: default(默认配置)和Manually select features(手动配置)默认配置只有babel和eslint其他的都要自己另外再配置,所以我们选第二项手动配置。在每次选择手动配置之后,会询问你是否保存配置,也就是图片中的koro选项,这样以后我们在进行创建项目的时候只需使用原先的配置就可以了,而不用再进行配置。3. 选择配置:根据你的项目需要来选择配置,空格键是选中与取消,A键是全选? Check the features needed for your project: (Press <space> to select, to toggle all, to invert selection) // 检查项目所需的功能:(按<space>选择,切换所有,反转选择)( ) TypeScript // 支持使用 TypeScript 书写源码 ( ) Progressive Web App (PWA) Support // PWA 支持 ( ) Router // 支持 vue-router ( ) Vuex // 支持 vuex ( ) CSS Pre-processors // 支持 CSS 预处理器。 ( ) Linter / Formatter // 支持代码风格检查和格式化。 ( ) Unit Testing // 支持单元测试。 ( ) E2E Testing4. 选择css预处理器:如果你选择了Css预处理器选项,会让你选择这个? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):// 选择CSS预处理器(默认支持PostCSS,Autoprefixer和CSS模块):SCSS/SASS LESS Stylus5. 是否使用路由的history模式:这里我建议选No,这样打包出来丢到服务器上可以直接使用了,后期要用的话,也可以自己再开起来。选yes的话需要服务器那边再进行设置。Use history mode for router? (Requires proper server setup for index fallback in production) // 路由使用history模式?(在生产环境中需要适当的服务器设置以备索引)6. 选择Eslint代码验证规则:> ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ESLint + Prettier7. 选择什么时候进行代码规则检测:建议选保存就检测,等到commit的时候,问题可能都已经积累很多了。之前写了篇VsCode保存时自动修复Eslint错误推荐一下。? Pick additional lint features: (Press <space> to select, to toggle all, to invert selection)( ) Lint on save // 保存就检测 ( ) Lint and fix on commit // fix和commit时候检查8. 选择e2e测试:? Pick a E2E testing solution: (Use arrow keys)❯ Cypress (Chrome only) Nightwatch (Selenium-based) 9. 把babel,postcss,eslint这些配置文件放哪:通常我们会选择独立放置,让package.json干净些? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)In dedicated config files // 独立文件放置 In package.json // 放package.json里10. 是否保存配置:Save this as a preset for future projects? (Y/n) // 是否记录一下以便下次继续使用这套配置// 选保存之后,会让你写一个配置的名字:Save preset as: name // 然后你下次进入配置可以直接使用你这次的配置了11. 下载依赖12. webpack配置的目录不见了:一起来看一下新项目的结构(下图),会发现2.x的webpack配置的目录不见了,也就是没有build、config这两个文件夹了:这种方式的优势对小白来说非常友好,不会一上来就两个文件夹,一堆文件,看着脑袋都大了。然后在引用抄别人的配置的时候,也非常方便,直接将文件复制过来就好了。在自定义一下webpack的配置,我们需要在根目录新建一个vue.config.js文件,文件中应该导出一个对象,然后进行配置,详情查阅官方文档// vue.config.jsmodule.exports = { // 选项…}还有一些小变动像:static文件夹改为public了,router文件夹变成了单个文件之类的(我之前一直这么做,嘿嘿)。13.启动项目:启动项目:npm run serve // 不是之前的 npm run dev打开http://localhost:8080:使用图形化界面创建/管理/运行项目:启动图形化界面vue ui 这是个全局的命令 在哪个文件夹都可以打开界面(下图),重要的项目可以收藏起来(置顶):创建项目和导入项目:目录选中之后,导入项目点击下面的导入就可以了。创建项目,填一个文件夹名字:然后选一下预先保存好的设置就可以了,非常方便,建议采用图形界面来创建项目:项目管理:当我们点击hello -cli3项目,就会进入项目管理的界面1. 仪表盘:这个仪表盘,主要是为了我们操作方便而设置的可以点击右上角的按钮,来添加/移动这些功能选项。2. vue-cli3.x插件:vue-cli3的插件功能,详情了解官方文档cli3插件安装的过程:3. 项目依赖直接在图形界面管理依赖很舒服了!安装依赖的时候,要记得选择开发依赖/运行依赖!4. 项目配置可以对cli进行一些配置、Eslint规则修改:5. 任务:serve 运行项目,点击直接运行,再也不用输入命令了!可以清楚的看到各个模块用了多久,方便我们针对性的进行优化:build 打包项目:这里主要展示了图表的功能,比以前2.x生成报告更加直观,超级棒!6. 其他夜间风格界面,我更喜欢这个界面直接打开编辑器,很棒了!还有一些乱七八糟的按钮结语可以说很认真了,希望大家看完能够有些收获,赶紧试试新版的vue-cli吧!希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。博客、前端积累文档、公众号、GitHub以上2018.11.10参考资料:vue-cli3官方文档vue-cli3.0搭建与配置 ...

November 15, 2018 · 2 min · jiezi

随我来基于webpack构建一个简易的vue脚手架 (webpack系列二)

前言之前有写了一篇webpack的文章(认识篇) 猛戳,大家对于webpack基本概念(entry,output,loader,plugin,mode…)应该是有了较模糊的认识.今天希望在通过(对于vue-cli的效仿)搭建一个自己的脚手架的途中对于概念会有更深刻的认识.目录1:搭建自己的项目模板(template) (基于vue的模板)2:生成对应的init命令,也就是脚手架构建命令以及上传NPM包 , 方便之后模板的使用 (分开俩篇来讲,方便你我 下一篇见)一: 模板构建先来个鸡汤 (这是个什么玩意啊怎么这么简单,我没问题分分钟掌握它) 摆正心态 那么follow me !!!初步构建mkdir my-vue-cli && cd my-vue-cli // 新建一个文件 并进入更目录 mkdir 是linux命令npm init -y // 初始一个packjage.json文件 -y 表示跳过询问步骤…完善项目结构//生成如下目录 ├── src //源目录(输入目录) │ ├── index.js │ ├── app.vue + |── index.html ├── package.json |── webpack.config.js //webpack配置文件 下载所需要的依赖(不太清楚的依次会介绍一下) npm install –save-dev vue 基于vue的那么vue必不可少…不多介绍 npm install –save-dev webpack 基于webpack的那么webpack也必不可少…不多介绍 npm install –save-dev webpack-cli webpack version 4+ 需要下载webpack-cli(一些指令下文可能涉及到) npm install –save-dev path path 模块提供了一些工具函数,用于处理文件与目录的路径。 npm install –save-dev html-webpack-plugin 简化了HTML文件的创建 Plugin that simplifies creation of HTML files to serve your bundles` npm install –save-dev clean-webpack-plugin 用于构建时清理构建文件夹下的内容 A webpack plugin to remove/clean your build folder(s) before building npm install –save-dev vue-loader Vue.js组件加载器(插件) npm install –save-dev vue-template-compiler 对于模板的函数编译 与vue-loader 配合使用 npm install –save-dev webpack-dev-server 热更新需要搭建服务模块项目代码构建src/index.jsimport Vue from ‘vue’ // 引入vue模块import App from ‘./app.vue’ //引入文件(组件) appnew Vue({ //vue写法 新建一个实例 el:"#app", //元素 template:’<App/>’, // 模板使用标签<app/> components:{App} // 组件app})src/app.vue<template> <div id=“app”> <p class=“test”>vue-cli-test vue-cli-test vue-cli-test </p> <p class=“test”>{{msg}}</p> </div></template><script>import Vue from ‘vue’ export default { name:‘app’, data(){ return { msg:“hello vue !!” } }, }</script><style > .test{ color:#020202; font-size:18px; }</style>webpack.config.jsconst path = require(‘path’); //path 模块提供了一些工具函数,用于处理文件与目录的路径。const HtmlWebpackPlugin = require(‘html-webpack-plugin’); //构建html文件const CleanWebpackPlugin = require(‘clean-webpack-plugin’); // 清理构建目录下的文件const webpack = require(‘webpack’); //webpack打包工具const VueLoaderPlugin = require(‘vue-loader/lib/plugin’); // vue-loader 编译vue文件const compiler = require(‘vue-template-compiler’) // 模板函数编译 与vue-loader配合使用module.exports = { entry: { //入口 “app”:"./src/index.js" }, module:{ //处理项目中的不同类型的模块。 rules:[ // rules 各种规则(数组类型) 每个规则可以分为三部分 - 条件(condition),结果(result)和嵌套规则(nested rule) { test:/.css/, use: [‘style-loader’, ‘css-loader’] // style-loader 和css-loader 编译css处理 }, { test: /.vue$/, loader: ‘vue-loader’ //vue-loader 编译vue模块 } ] }, devtool: ‘inline-source-map’, //生曾map 映射对应代码 方便错误查询 devServer:{ contentBase: ‘./dist’, // 告诉服务从哪提供代码内容(静态文件这么使用) hot:true //hot模式开启 }, plugins:[ new CleanWebpackPlugin([‘dist’]), // 告诉清理插件的目录 new HtmlWebpackPlugin({ // 构建html filename:‘index.html’, //文件名 title:‘my-vue-cli’, //title template:’./index.html’, //参照模板样式 }), new webpack.HotModuleReplacementPlugin(), //热模块替换开启 new VueLoaderPlugin() //vue-loader插件开启 ], output: { //出口 filename: ‘index.js’, //文件名 path: path.resolve(__dirname, ‘dist’), //路径 publicPath:"" //srcript 引入路径 }, resolve:{ //引入路径是不用写对应的后缀名 extensions: [’.js’, ‘.vue’, ‘.json’], alias:{ //正在使用的是vue的运行时版本,而此版本中的编译器时不可用的,我们需要把它切换成运行时 + 编译的版本 ‘vue$’:‘vue/dist/vue.esm.js’, //用@直接指引到src目录下 ‘@’: path.resolve(__dirname,’./src’), } }, };package.json添加script命令"scripts": { “test”: “echo "Error: no test specified" && exit 1”, “watch”: “webpack –watch”, “dev”: “webpack-dev-server –open –hot”, “build”: “webpack”},npm run dev 运行于8080/可看到预期效果.npm run build 打包编译同样可以看到效果 skr~~~~~~~~~github代码仓库,猛戳结尾本篇只是介绍基于vue,webpack的vue-cli简易搭建(因为init构建命令这些说好讲是好讲,但是讲太粗怕大家不太明白,那不如单独拿一篇出来让大家看),根据本文大家可以根据需求进行完善搞一个自己的脚手架.之后用自己的开发..是不是想想挺美~~ 快去行动吧.ps:想提前看看构建命令效果的同学 > git仓库,戳一戳把ps:(有我讲的不明白的地方,评论区见.我来完善) ...

November 12, 2018 · 2 min · jiezi

vue组件通信全面总结

写在前面组件间的通信是是实际开发中非常常用的一环,如何使用对项目整体设计、开发、规范都有很实际的的作用,我在项目开发中对此深有体会,总结下vue组件间通信的几种方式,讨论下各自的使用场景文章对相关场景预览父->子组件间的数据传递子->父组件间的数据传递兄弟组件间的数据传递组件深层嵌套,祖先组件与子组件间的数据传递文章相关技术预览prop、emit、bus、vuex、路由URL、provide/inject注:以下介绍与代码环境:vue2.0+、vue-cli2父->子组件间的数据传递父子组件的通信是开发是最常用的也是最重要的,你们一定知道父子通信是用prop传递数据的,像这样://父组件,传递数据<editor :inputIndex=“data” :inputName=“王文健”></editor>//子组件,接受数据,定义传递数据的类型type与默认值default props: { inputIndex: { type: Object, default: function(){ return {} } }, inputName: { type: String, default: ’’ },注意项:父组件传递数据时类似在标签中写了一个属性,如果是传递的数据是data中的自然是要在传递属性前加v-bind:,如果传递的是一个已知的固定值呢字符串是静态的可直接传入无需在属性前加v-bind数字,布尔,对象,数组,因为这些是js表达式而不是字符串,所以即使这些传递的是静态的也需要加v-bind,把数据放到data中引用,如果prop传到子组件中的数据是一个对象的话,要注意传递的是一个对象引用,虽然父子组件看似是分离的但最后都是在同一对象下如果prop传到子组件的值只是作为初始值使用,且在父组件中不会变化赋值到data中使用如果传到子组件的prop的数据在父组件会被改变的,放到计算属性中监听变化使用。因为如果传递的是个对象的话,只改变下面的某个属性子组件中是不会响应式更新的,如果子组件需要在数据变化时响应式更新那只能放到computed中或者用watch深拷贝deep:true才能监听到变化当然如果你又需要在子组件中通过prop传递数据的变化做些操作,那么写在computed中会报警告,因为计算属性中不推荐有任何数据的改变,最好只进行计算。如果你非要进行数据的操作那么可以把监听写在watch(注意deep深拷贝)或者使用computed的get和set如下图:但问题又来了,如果你传进来的是个对象,同时你又需要在子组件中操作传进来的这个数据,那么在父组件中的这个数据也会改变,因为你传递的只是个引用, 即使你把prop的数据复制到data中也是一样的,无论如何赋值都是引用的赋值,你只能对对象做深拷贝创建一个副本才能继续操作,你可以用JSON的方法先转化字符串在转成对象更方便一点,所以在父子传递数据时要先考虑好数据要如何使用,否则你会遇到很多问题或子组件中修改了父组件中的数据,这是很隐蔽并且很危险的子->父组件间的数据传递在vue中子向父传递数据一般用$emit自定义事件,在父组件中监听这个事件并在回调中写相关逻辑// 父组件监听子组件定义的事件 <editor :inputIndex=“index” @editorEmit=‘editorEmit’></editor>// 子组件需要返回数据时执行,并可以传递数据this.$emit(’editorEmit’, data)那么问题来了,我是不是真的有必要去向父组件返回这个数据,用自定义事件可以在当子组件想传递数据或向子组件传递的数据有变化需要重新传递时执行,那么另外一种场景,父组件需要子组件的一个数据但子组件并不知道或者说没有能力在父组件想要的时候给父组件,那么这个时候就要用到组件的一个选项ref:<editor ref=“editor” @editorEmit=‘editorEmit’></editor>父组件在标签中定义ref属性,在js中直接调用this.$refs.editor就是调用整个子组件,子组件的所有内容都能通过ref去调用,当然我们并不推荐因为这会使数据看起来非常混乱,所以我们可以在子组件中定义一种专供父组件调用的函数,,比如我们在这个函数中返回子组件data中某个数据,当父组件想要获取这个数据就直接主动调用ref执行这个函数获取这个数据,这样能适应很大一部分场景,逻辑也更清晰一点另外,父向子传递数据也可以用ref,有次需要在一个父组件中大量调用同一个子组件,而每次调用传递的prop数据都不同,并且传递数据会根据之后操作变化,这样我需要在data中定义大量相关数据并改变它,我可以直接用ref调用子组件函数直接把数据以参数的形式传给子组件,逻辑一下子清晰了如果调用基础组件可以在父组件中调用ref执行基础组件中暴露的各种功能接口,比如显示,消失等兄弟组件间的数据传递vue中兄弟组件间的通信是很不方便的,或者说不支持的,那么父子组件中都有什么通信方式呢路由URL参数在传统开发时我们常常把需要跨页面传递的数据放到url后面,跳转到另外页面时直接获取url字符串获取想要的参数即可,在vue跨组件时一样可以这么做,// router index.js 动态路由{ path:’/params/:Id’, component:Params, name:Params}// 跳转路由<router-link :to="/params/12">跳转路由</router-link>在跳转后的组件中用$route.params.id去获取到这个id参数为12,但这种只适合传递比较小的数据,数字之类的Bus通信在组件之外定义一个bus.js作为组件间通信的桥梁,适用于比较小型不需要vuex又需要兄弟组件通信的bus.js中添加如下 import Vue from ‘vue’ export default new Vue组件中调用bus.js通过自定义事件传递数据 import Bus from ‘./bus.js’ export default { methods: { bus () { Bus.$emit(‘msg’, ‘我要传给兄弟组件们’) } } }兄弟组件中监听事件接受数据 import Bus from ‘./bus.js’ export default { mounted() { Bus.$on(‘msg’, (e) => { console.log(e) }) } }注:以上两种使用场景并不高所以只是简略提一下,这两点都是很久以前写过,以上例子网上直接搜集而来如有错误,指正Vuex集中状态管理vuex是vue的集中状态管理工具,对于大型应用统一集中管理数据,很方便,在此对vuex的用法并不过多介绍只是提一下使用过程中遇到的问题规范:对于多人开发的大型应用规范的制定是至关重要的,对于所有人都会接触到的vuex对其修改数据调用数据都应有一个明确严格的使用规范vuex分模块:项目不同模块间维护各自的vuex数据限制调用:只允许action操作数据,getters获取数据,使用mapGetters,mapActions辅助函数调用数据对于vuex的使用场景也有一些争论,有人认为正常组件之间就是要用父子组件传值的方式,即使子组件需要使vuex中的数据也应该由父组件获取再传到子组件中,但有的时候组件间嵌套很深,只允许父组件获取数据并不是一个方便的方法,所以对于祖先元组件与子组件传值又有了新问题,vue官网也有一些方法解决,如下祖先组件与子组件间的数据传递provide/inject除了正常的父子组件传值外,vue也提供了provide/inject这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效官网实例// 父级组件提供 ‘foo’var Provider = { provide: { foo: ‘bar’ }, // …}// 子组件注入 ‘foo’var Child = { inject: [‘foo’], created () { console.log(this.foo) // => “bar” }}provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。一个字符串数组,或一个对象,对象的 key 是本地的绑定名,value 是:在可用的注入内容中搜索用的 key (字符串或 Symbol),或 一个对象,该对象的:from 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol)default 属性是降级情况下使用的 value提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。具体细节移步vue相关介绍https://cn.vuejs.org/v2/api/#…provide/inject还未在项目中应用过,后面会做尝试写在结尾文章只是整理一下笔记,谈一谈遇到的问题和经验,并没有严谨的措辞和详细的过程,如有错误望指正原创文章转载引用请注明原文链接http://blog.wwenj.com/index.p… ...

October 10, 2018 · 1 min · jiezi

vue生命周期简记

写在前面如题,vue的生命周期是vue理解的重要一环,弄明白它不只会加深对vue的理解,在实际开发中也会更加得心应手,在此记录写项目开发中体会到的一些经验,只是总结 并不全面,想要详细学习网上关于vue生命周期学习的文章很丰富<!–more–>生命周期beforeCreate:创建前,new操作,已绑定事件,还没有数据,不能处理数据created:创建,可以处理数据beforeMounted:挂载前,准备把创建的挂载到el上,有el才去执行挂载 挂载前的el仍是<div id=“app”></div>有template转化后执行render funcion再去渲染cli中的.vue文件中的template是通过vue-loader直接转化直接执行render funcion的减少耗时mounted:挂载,挂载后$el就是渲染后的<div>123</div>,挂载前后中间执行render funcionbeforeUpdated:更新前,数据变化updated:更新beforeDestroy:销毁前,组件被销毁,或手动销毁destroy:销毁注: 挂载前的钩子里获取不到el的,想对如data中的数据进行操作最早要在created里;一般在created和mounted钩子里写一些页面渲染前的操作,比如数据请求;有些传统的第三方库在使用后需要在组件销毁时手动删除其创建的对象在destroy中<!–more–>没有代码最后附上一张以前看到的一张图

October 10, 2018 · 1 min · jiezi

[vuejs 踩坑实战系列] keep-alive 被 beforeRouteEnter 骗了

大家中秋假期快乐,假期分享一些实战文章给大家,原创不易,欢迎转发,一起学习现在大家基本都在单页应用里面使用了 keep-alive 来缓存不活动的组件实例,而不是销毁它们。如果你还没有使用,可以看看官方的介绍(如果大家需要一些新手入门的文章可以留言哈):https://cn.vuejs.org/v2/api/#…用法很简单:主要是包裹<keep-alive> …</keep-alive>使用场景:和单页应用环境配合使用的:<keep-alive> <router-view v-if="$route.meta.keepAlive"></router-view></keep-alive>有以下几个常识,如果你还没有使用 keep-alive 的话,可以记下来:1、组件内的第一次的生命周期:mounted ==> activated2、切换路由再次进来只会触发 activated3、可以通过 router 的钩子函数 beforeRouteEnter 来做一些辅助判断具体可以看看官方的这个的文档:https://router.vuejs.org/zh/g…不能获取组件实例 this比如你要设置 data 里面的变量,抱歉,这里操作不了,那如何做呢?很多熟悉的人会想到 next 操作 vm 对象:beforeRouteEnter (to, from, next) { next(vm => { // 通过 vm 访问组件实例 })}是的,这里你可以通过 from.name 来做一些判断,比如如下代码片段:需求很简单,判断一下从特定路由切换过来,做一个判断赋值给 data 的 isFromTesterbeforeRouteEnter (to, from, next) { console.log(to, from); if (from.name == ‘Tester’) { next(vm => { vm.isFromTester = true }) } else { next(vm => { vm.isFromTester = false }) }}然后你就可以在 activated 生命周期直接判断啦activated () { if (this.isFromTester) { //… }}大功告成啦抱歉,这里的 activated 不会那么及时地更新 isFromTester,所以第一次你获取的不是 true,第二次是可以的那我们就要来刨根问底了,到底为啥不是及时更新的呢?有没有人想到了 vue 里面一个很常见的 nextTick 这个东西?是滴,就是它,它骗了 activated,真相在这里:(我们省去了很多路由事件里面自己的处理逻辑和 vue activated 的 hook 的触发) ...

September 24, 2018 · 1 min · jiezi

[vuejs 踩坑实战系列] 路由场景下父子组件的生命周期顺序来个刨根问底

大家中秋假期快乐,假期分享一些原理设计文章给大家原创不易,欢迎转发,一起学习(凌晨写的,不容易哈,收藏或者点个赞吧)在常见的单页应用中,我们都会有一个根 App.vue 文件,里面放置一个 router-view 然后配置路由来切换.很多人在子父组件嵌套关系下的生命周期钩子函数如何应用,谁先谁后(比如哪个用来发送请求,数据传递)等有所疑问。本文聚焦 mounted 事件(需要 created 的可以留言哈),先抛结论:子组件一层一层往外触发,最终触发根 App.vue 的 mounted验证的做法很简单:你只需要在每一个组件里面的 mounted 增加打印日志就可以看到了,我们具体来看看设计原理现在假设我们配置了路由:一级是 /user/:id 二级是 profileconst router = new VueRouter({ routes: [ { path: ‘/user/:id’, component: User, children: [ { // 当 /user/:id/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: ‘profile’, component: UserProfile } ] } ]})先看一下所有的 mounted 最终在各自组件里面是如何被调用的:通过 vm.$options 获取组件内部的配置,然后通过 call 方法下面的我们又会遇到 vnode(所以掌握它很重要,在前面的 vue.js 源码原创系列 ref 与 $refs 如何关联 也提到了一些 vnode,感兴趣可以看看),就是下面 componentVNodeHooks 里面的 insert 函数的参数var componentVNodeHooks = { insert: function insert (vnode) {}}里面呢,第一步:从 vnode 里面获取 componentInstancevar componentInstance = vnode.componentInstance;然后判断 _isMounted 是否已经执行过 mounted (很常用的状态二次确定的变量,前面的 _ 一般代表内部使用)if (!componentInstance._isMounted) { componentInstance._isMounted = true; callHook(componentInstance, ‘mounted’);}上面我们就用到了 callHook 函数了,传入的第二个参数也正是本文讨论的生命周期的 mounted再往后有一个 invokeInsertHook 函数function invokeInsertHook (vnode, queue, initial) {}注意一下源码里面的注释:delay insert hooks for component root nodes, invoke them after the element is really inserted设置了 pendingInsert(后面会在 initComponent 中使用),代码如下:if (isTrue(initial) && isDef(vnode.parent)) { vnode.parent.data.pendingInsert = queue;}内部设计:循环 queue 这个包含 vnode 的数组对象,如图所示:注意一下标注的 data.hook,下面的代码片段会使用到,也就是调用上面提到的 componentVNodeHooks 对象的 insert:for (var i = 0; i < queue.length; ++i) { queue[i].data.hook.insert(queue[i]);}再往下,带着疑问:这个 queue 是如何生成 vnode 数组的呢?最开始定义一个空数组:var insertedVnodeQueue = [];在刚才的 对象中还有 initvar componentVNodeHooks = { init: function init () { // … }}init 函数内部定义一个 childvar child = vnode.componentInstance = createComponentInstanceForVnode(…)然后会调用一个 $mountchild.$mount(hydrating ? vnode.elm : undefined, hydrating);在函数 initComponent 中会用到之前的 pendingInsert,而且 insertedVnodeQueue 这个数组对象会调用 push 插入元素function initComponent (vnode, insertedVnodeQueue) { if (isDef(vnode.data.pendingInsert)) { insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert); vnode.data.pendingInsert = null; }}在函数 invokeCreateHooks 内部insertedVnodeQueue 这个数组对象会调用 push 插入元素function invokeCreateHooks (vnode, insertedVnodeQueue) { i = vnode.data.hook; // Reuse variable if (isDef(i)) { if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); } }}在函数 mountComponent 内部当 vm.$vnode 为 null 也会调用 callHook,第二个参数传入 mountedfunction mountComponent () { if (vm.$vnode == null) { vm._isMounted = true; callHook(vm, ‘mounted’); }} ...

September 24, 2018 · 2 min · jiezi