⭐️ ? ✨ ⚡️
技术栈
# vue官网
http://vuejs.org/
# Vuex中文手册
http://vuex.vuejs.org
# Vue-Router 手册
http://router.vuejs.org
# 最全Vue资源大全
https://github.com/opendigg/awesome-github-vue
# vue-devtool 调试vue必备chrome工具
https://github.com/vuejs/vue-devtools
# vue + typescript
https://github.com/HerringtonDarkholme/vue-ts-loader
# webpack2 中文文档
http://www.css88.com/doc/webpack2/guides/code-splitting-require/
# Vuejs 实用技巧
https://zhuanlan.zhihu.com/p/25589193
# Vue.js 实用技巧(二)
https://zhuanlan.zhihu.com/p/25623356
# 饿了么基于Vue 2.0的通用组件库开发之路
https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247484467&idx=1&sn=8643c5945adb151db9c6fe757cd6adfa&chksm=972366f1a054efe733e01069b2adb81d453a2c30bbc5329c77948e8160090294bf14918381a1&scene=21#wechat_redirect
⭐️ ? ✨ ⚡️ Vue最好的UI库
# PC端
http://element.eleme.io/
# 移动端
https://github.com/ElemeFE/mint-ui
第三方插件收集:
# vue-lazyload 一个简单易用的 Vue 图片延迟加载插件 https://github.com/hilongjw/vue-lazyload?from=gold
# 表单验证插件
https://github.com/QingWei-Li/vuerify
# 前端国际化
https://github.com/kazupon/vue-i18n
# swpier强大且令人羡慕的拖拽组件
https://github.com/surmon-china/vue-awesome-swiper
安装
注意 :由于部分模块是需要跨域才能下载,所以这里需要使用cnpm install,cnpm是淘宝版本的npm,需要另外下载:http://www.cnblogs.com/CyLee/p/5719929.html
# 全局安装 vue-cli $ npm install -g vue-cli
# 创建一个基于 "webpack" 模板的新项目,注意,Use ESLint to lint your code? (Y/n) n $ vue init webpack my-project
# 安装依赖,走你 $ cd my-project
# 使用cnpm进行安装 $ cnpm install
# 运行,并且自动进入热编译 $ npm run dev
端口可以在config/index.js 中修改
我们启动页看到的文件源代码是:src/App.vue
在build/webpack.base.conf.js 中搜索 jsLint 关键词注释掉相关的代码即可关闭 jsLint 代码严格模式
Vuejs不兼容低版本浏览器,比如IE8、360浏览器兼容模式等
神坑与碎片化知识点记录:
86、watch route 无法生效的原因?
可能是因为父子路由的原因吧。建议用以下几个方案代替:
watch: { '$route': { deep: true, handler (newV, oldV) { this.$store.dispatch('Map/reset') } } }, 2、如果还是不行的话,用路由钩子: beforeRouteEnter (to, from, next) { next(vm => { if (from.path === '/myBusiness') vm.$router.push('/') next() }) } // 页面离开的时候,初始化一些参数配置 beforeRouteLeave (to, from, next) { // 还原为全部选择框都显示 this.$store.dispatch('list/onlyShowSelect') // 取消问题类型,默认为空 this.$store.dispatch('list/eq_problemType') next(); },
85、vue编译插件,可以发布给人使用
$ vue-cli-service build --target lib --name w-basic-layout --dest lib packages/index.js
"scripts": {
"lib": "vue-cli-service build --target lib --name w-basic-layout --dest lib packages/index.js"
},
84、vuex 如果要dispatch另一个模块的actions时怎么办? 只需要加入 {root: true} 即可
dispatch('Map/fuck', 'shit', {root: true})
83、vue 关于deep watch / computed 监听不到 vuex state 对象变化的的问题
// 超简易拷贝(如果是深拷贝还多此一举把get/set拷贝进去了,所以用简易拷贝即可) let __VALUE__ = JSON.parse(JSON.stringify(state.problemReply)) // 加入部门回复详情 __VALUE__[orderId] = data.problemReply // 更新,只能这样一波骚操作才能让computed和watch监听到。具体原因我稍后学习o(╥﹏╥)o。 state.problemReply = __VALUE__
82、 如何在index.html加载本地js文件?
将文件放置在 /static 中 ,编译的时候会一同打包在dist/static/ 中,所以你就可以在index.html中使用
<script src="./static/echarts.min.js">script>
81、深度作用选择器:https://vue-loader-v14.vuejs.org/zh-cn/features/scoped-css.html
vue组件会为template中的每个html元素加入 [data-v-xxxx] 属性来确保 css 作用本组件而不会污染全局,而如果你引用了第三方组件,默认只会对组件的最外层(div)加入这个属性,但第二层开始就没有效果了。如图所示: 第一层还有 data-v-17bb9a05, 但第二层的 .weui-cells 就没有了。
如果你希望通过如下方式修改 weui-cells。是没有效果的
除非你将scoped移出。或者新建一个没有scoped的style(一个.vue文件允许多个style)。这是因为,所有的scoped中的css最终编译出来都会变成这样:
.fuck[data-v-17bb9a05] .weui-cells[data-v-17bb9a05]
解决方法还有另一个,那就是深度作用选择器:
重要的事情说三遍,如果你是scss之类的预编译css的话, >>> 要换成 /deep/
重要的事情说三遍,如果你是scss之类的预编译css的话, >>> 要换成 /deep/
重要的事情说三遍,如果你是scss之类的预编译css的话, >>> 要换成 /deep/
80、mapState的使用。其中 theme 是模块名。当然也可以忽略。
import { mapState } from 'vuex' computed: { ...mapState('theme', ['findLastSixMonthAir', 'findLastSixMonthAirTown']) },
79、computed + watch + vuex的组合,虽然一时爽,但也有问题,就是数据嵌套的太深的时候,没办法更新UI。又不可以用$set方案来解决。
但也有办法,那就是深拷贝赋值,然后在某一层赋值,还是可以更新UI的。
// 超简易拷贝(如果是深拷贝还多此一举把get/set拷贝进去了,所以用简易拷贝即可) const v = JSON.parse(JSON.stringify(state.list.data)) // 加入回复字段 v[index].problemReply = data.problemReply // 更新,只能监听到data属性,所以必须这样操作。不能这样: state.list.data[index].problemReply = data.problemReply state.list.data = v
78、由于一些嵌套特别深的数据,导致数据更新了。UI没有更新,我捉摸着有没有和react一样的立即更新UI的API呢 this.forceUpdate()呢?结果还真有:
this.$forceUpdate();
77、使用
76、ElementUI 组件在 非.vue文件的使用,譬如router.js 文件中
import { Message } from 'element-ui'; Message('这是一条信息 );
75、props如何双向属性绑定?
74、vue 中使用scoped关键字后样式不能修改第三方组件问题
https://blog.csdn.net/mr_hexs/article/details/80375244
73、vue动画的使用。
https://cn.vuejs.org/v2/guide/transitions.html
.cell-enter-active, .cell-leave-active { transition: all 1s; }
/* 新成员进入时的动画 */ .cell-enter, .cell-leave-to /* .cell-leave-active below version 2.1.8 */ { /*opacity: 0;*/ transform: translateY(300px); } /* 所有项移动时的动画钩子 */ .cell-move { transition: transform 1s ease; }"cell" tag="div" class="transition-group"> for='(item, index) in items' :key='item' class="cell"> {{ item.title }}
72、史诗级的神坑:v-for循环中,我习惯这样使用index:v-for='(item, index) in items' :key='index'
当我对items数组的前面插入数据时:
this.items.unshift({title: '重大事件', time: '2018.08.08 10:00', content: '涡领商业西街食物中毒', num: '1'})
由于加入了transition-group 动画,我可以很清楚的看见插入的动画效果,却一直是push的效果。后来查看官方demo,才发现这个:key需要这样设置:
v-for='(item, index) in items' :key='item'
71、v-cloak 用来解决渲染之前的尴尬期
https://segmentfault.com/a/1190000008819667
70、vue v-model 与 组件化的表单组件如何沟通
参考mint-ui的代码:
https://github.com/ElemeFE/mint-ui/blob/master/packages/radio/src/radio.vue
https://github.com/ElemeFE/mint-ui/blob/master/packages/field/src/field.vue
Document for='(item, index) in items' :key='index'>
Picked: {{ picked }}
69、input 聚焦的时候键盘盖住
function getElementTop(element){ try { var actualTop = element.offsetTop; var current = element.offsetParent; while (current !== null){ actualTop += current.offsetTop; current = current.offsetParent; } return actualTop; } catch (e) {} } setTimeout(() => { window.scrollTo(0, getElementTop(e.target)); }, 150)
68、注册全局指令
https://cn.vuejs.org/v2/guide/custom-directive.html
// v-auth 全局权限指令,但暂时没有想好怎么处理 Vue.directive('auth', { inserted: function (el, node) { // 从 store 中获取权限信息 var auth = store.state.auth.authInfo // 遍历权限列表 for (var i = 0; i < auth.length; i++) { // 如果符合条件,那么就把它删除 if (auth[i].resContent === node.value) { // 删除元素 return el.parentNode.removeChild(el) } } } })
67、新的错误出现:Module build failed: Error: No parser and no file path given, couldn't infer a parser.
https://segmentfault.com/q/1010000015052538
运行:npm i prettier@~1.12.0
66、Vue自带的错误捕获机制
https://cn.vuejs.org/v2/api/#errorHandler
Vue.js 2.2.0+提供了 errorHandler, (一般在src目录的main.js文件中配置)
Vue.config.errorHandler = function(err, vm, info) { console.log(err, vm, info); };
65、fetch中response.json()的问题
1、你不能连续使用两次response.json(),否则会报错
2、你不能直接在promise中打印出console.log(response.json()),打印不出的。所以你必须这样
var json = response.json() json.then(_=>{ console.log(_) }) return json
64、webpack-dev-server 无法通过ip访问的问题
http://www.cnblogs.com/CyLee/p/8376648.html
63、路由的限制和next的问题。
if (needLoginPage.indexOf(to.fullPath.replace(/\/|\\/g, '').toLocaleLowerCase().trim()) >= 0 && !store.state.token) { // 史诗级神坑,这里必须先next,否则会一直返回不了, // 不要问我为什么,我猜测是,由于你缺少了一次next,一直卡着不给后退。所以这里无论如何也需要next一下. Toast('请先登录') // next() // 设置去路 return store.dispatch('set_wantTo', to.path).then(_ => { // 跳转到登录页 router.push('/login') // 继续渲染它? return next() }) }
62、分享一个小技巧,如果项目迁移、而node_modules迁移有问题,项目启动不了的时候,可以选择完全删除node_modules。然后重点来了
不要使用window内置的powser shell,而是使用一些高亮的shell工具如cmder。执行cnpm install试试。如果还是不行,继续重复删掉node_modules,反复执行cnpm install。记得要用cmder哦
61、学完局部注册和全局注册的差别后,你再看看main.js的new Vue代码,你应该懂得了
绑定在html上的一个叫#app的元素上
基本上就是这样的原理
60、Vue warn]: Do not use built-in or reserved HTML elements as component id: button
将组件的name属性从button改为mybutton即可。
export default { name: 'mybutton', // ... }
59、webpack 使用别名(resolve.alias)解决scss @import相对路径导致的问题
58、vue 更新了vue-cli到最新版本后引发的问题: require和import、vue-loader的问题
结局:[email protected] 降级到 [email protected] 即可解决
57、手动挂载$mount()
如果没有挂载的话,没有关联的 DOM 元素。是获取不到$el的。
https://vuejs.org/v2/api/#vm-mount
var MyComponent = Vue.extend({ template: 'Hello!' }) // create and mount to #app (will replace #app) new MyComponent().$mount('#app') // the above is the same as: new MyComponent({ el: '#app' }) // or, render off-document and append afterwards: var component = new MyComponent().$mount() document.getElementById('app').appendChild(component.$el)
56、销毁组件
// get~ 销毁组件 destroyElement() { this.$destroy(true); this.$el.parentNode.removeChild(this.$el); },
55、vue.extend 局部注册 的应用2
请注意,extend创建的是一个组件构造器,而不是一个具体的组件实例。所以他不能直接在new Vue中这样使用: new Vue({components: fuck})
最终还是要通过Vue.components注册才可以使用的。
doctype html>
<html>
<head>
<meta charset="utf-8">
<title>在Vue中注册组件title>
head>
<body>
<div id="app">
<todo :todo-data="groceryList">todo>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue " type="text/javascript">script>
<script>
/**
* 请注意,extend创建的是一个组件构造器,而不是一个具体的组件实例。
* 所以他不能直接在new Vue中这样使用: new Vue({components: fuck})
* 最终还是要通过Vue.components注册才可以使用的。
*/
// 构建一个子组件
var todoItem = Vue.extend({
template: ` <li> {{ text }} </li> `,
props: {
text: {
type: String,
default: ''
}
}
})
// 构建一个父组件
var todoWarp = Vue.extend({
template: `
<ul>
<todo-item
v-for="(item, index) in todoData"
v-text="item.text"
></todo-item>
</ul>
`,
props: {
todoData: {
type: Array,
default: []
}
},
// 局部注册子组件
components: {
todoItem: todoItem
}
})
// 注册到全局
Vue.component('todo', todoWarp)
new Vue({
el: '#app',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
script>
html>
54、vue.extend 局部注册 的应用1
请注意,在实例化extends组件构造器时,传入属性必须是propsData、而不是props哦
另外,无论是Vue.extend还是Vue.component 里面的data定义都必须是函数返回对象,如 Vue.extend({data: function () {return {}}})。除了new Vue可以直接对data设置对象之外吧,如 new Vue({data: {}});
doctype html>
<html>
<head>
<meta charset="utf-8">
<title>在Vue中注册组件title>
head>
<body>
<div id="todoItem">div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue" type="text/javascript">script>
<script>
// 局部注册组件
var todoItem = Vue.extend({
data: function () {
return {
todoData: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
},
template: `
<ul>
<li v-for='(d, i) in todoData' :key="i">
{{ d.text }}
</li>
</ul>
`
});
// 请注意,在实例化extends组件构造器时,传入属性必须是propsData、而不是props哦
new todoItem({
propsData: {
todoData: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
}).$mount('#todoItem')
script>
html>
53、Vue的实例属性
52、从来没使用过 的使用方式。
一直都是工程化来开发的。现在也开始使用一下吧。
index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js">script>
<script src="./Components/Counter.js">script>
head>
<body>
<div id="app">
<p>总数量: {{ total }}p>
<counter @add="handleGetTotal" @reduce="handleGetTotal">counter>
div>
body>
<script>
new Vue({
el: '#app',
data: {
total: 0
},
methods: {
handleGetTotal: function (v) {
this.total = v;
}
}
})
script>
html>
Components/Counter.js
Vue.component('counter', { template: ``, data: function () { return { counter: 0 } }, methods: { handleAddNum: function (){ this.counter++; this.$emit('add', this.counter); }, handleReduceNum: function (){ this.counter--; this.$emit('reduce', this.counter); } } })
51、enter事件,经常用但经常忘记。还是记录一下把
@keyup.enter="enterHandle"
50、简单的知识点,如何调用子组件的函数Methods
答案就是使用ref即可。
ref="countdown"> beforeDestroy () { // 切换页面时消灭计时器 this.$refs.countdown.clearTimer() }
49、使用vue-cli 配置 proxyTable 代理地址,实现跨域问题
路径在/config/index.js 中,找到dev.proxyTable。如下配置示例:
proxyTable: { '/api': { // 我要请求的地址 target: 'http://oatest.bujidele.com:8010/apitest/api/tydproject/doOld/', //是否跨域 changeOrigin: true, // 重写地址 pathRewrite: { '^/api': '/' } } }
那么当我们请求 http://localhost:8888/api/ 的时候,就等于请求了 http://oatest.bujidele.com:8010/apitest/api/tydproject/doOld/
请注意上面的【pathRewrite】字段。这是什么意思呢。
我们再来看看下面的例子
proxyTable: { '/api': { // 我要请求的地址 target: 'http://192.168.14.29:31006/xindai/', //是否跨域 changeOrigin: true, // 重写地址 pathRewrite: { '^/api': '/api' } } },
如果是这样的话,当我们请求/api的时候,就等于请求了http://192.168.14.29:31006/xindai/api
这要看你要不要了,如果不要的话,直接换为'^/api': '/' 就好了
48、获取组件自己在父组件中的索引。
内置属性即可。this.index 。
随便一提这些内置的属性应该多记一点,譬如this.$parent.xxxx 这些都是很实用的
47、通过js新建组件并且传入props:
import Vue from 'vue'; import product from './index.vue'; let productComponent = Vue.extend(product); export default (food, isShow, idx, ratings) => { new productComponent({ el: '#goodss', propsData: { food, isShow, idx, ratings } }); }
46、axios 加入header之后,请求出现
Failed to load http://localhost:8080/team.php: Request header field x-jwt-header is not allowed by Access-Control-Allow-Headers in preflight response.
//POST传参序列化(添加请求拦截器) axios.interceptors.request.use(config => { config.headers['x-jwt-header'] = localStorage.token return config; },error =>{ alert("错误的传参", 'fail') return Promise.reject(error) })
原因是后端没有开启对header的允许。php中输入以下代码即可:
header('Access-Control-Allow-Headers:x-jwt-header,content-type');
45、webpack 打包压缩 ES6文件报错UglifyJs + Unexpected token punc ((); 或者 Unexpected token: operator (>)
解决方案就是将babel配置转义到 .babelrc 文件中。具体做法是在根目录新建 .babel,输入
{ "presets": ["es2015"] }
在webpack加载babel-loader的时候会自动加载.babelrc配置的。
44、如果不能看到源码来修复bug真心累。除非是通过场景重现的bug。否则定位不到错误行很惨。所以需要开启map。
在 /config/index.js 中,修改 productionSourceMap 为 true 即可。
43、尽管加入了babel-polyfill ,依然出现 【ReferenceError: Promise is not define】的问题。目前只在三星、金立手机出现这种问题。没办法,只能强行修复了。
npm install promise --save-dev
window.Promise = require('promise');
记得清除一下手机的缓存。
42、记一起和前端没什么卵关系的后端405问题
问题的关键点在于本来是POST请求,会变成OPTION请求,并且提示405报错,会类似跨域。并且只有某些手机机型才会(如Oppo系列)。
其实跨域的问题,如果在PHP只需要在头设置允许跨域即可。其他语言也类似。
header("Access-Control-Allow-Origin:*"); 或者 header("Access-Control-Allow-Origin:url地址");
但.net据说也设置大致如上设置了,却不能轻易跨域,在开发环境中我甚至需要开启代理或浏览器非安全模式才可以跨域。
直到今天这个问题在线上彻底爆发出来。才认真研究。其原因就在于.net web.conf配置中,需要注释或删除这一句配置:
于是具体配置大致如下:
<handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> handlers> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="*" /> <add name="Access-Control-Allow-Headers" value="Content-Type" /> customHeaders> httpProtocol>
问题即可解决。无论线上和本地都可以顺利跨域了。其实就是个跨域的问题罢了,记录一下
41、记一次饿了么UI图标加载无效的问题。并且提示错误
Failed to decode downloaded font:
很显然,问题在于webpack的loader中。检查了一下发现有两个相同的file-loader的配置,删除其中一个即可。
40、记一次编译没反应、无进度、没有任何报错的提示,但后台却TM一直消耗内存的BUG:
控制台一直提示“building for production...”,而且spinner停止了动画!
由于没有任何的提示。况且项目的代码、结构、设计完全未知模糊的情况下,我只能按照unix的理念“使之运行、使之正确、使之快速”作为理论依据指导我来调试了。
按照这个理念,我最初设定的目标,就是让他正常的编译,无论结果是如何。所以我将main.js(入口文件)的代码尽可能删掉。只保存纯洁无比的app.vue组件。以及vue的初始化。就这样运行编译,果然可行!!而后我通过按照二分法。一步一步还原并删减一半的代码,运行npm run build。一步一步尝试。直到定位到最小单元的文件(result.vue),在这个文件中只有三个剑客,分别是template、script、css。他们一样没有逃过我的魔爪,一步步的删减,直到最后定位到了是css文件的错误。一个css文件居然能引发这种史诗级的错误?通过继续对css文件中的css代码进行二分法删减,最后发现。怎么会有一个“|”符号?于是把它删除,正常编译!
结果和过程都是美好的。但我觉得我这种调试方式也值得记录。
39、组件 vue-awesome-swiper 的坑
1、在vertical的场景模式下,默认的高度很奇怪,非常非常的大。完全没有规律。后来使用autoHeight好了一点。但依然有问题,问题在于它会根据swiper-slide内元素的高度自动变化叠加。依然会非常非常大。最后才知道。手动设置height即可解决。由于我的场景是fullpage页面,所以只需要设置height : window.innerHeight 即可。完整代码如下:
swiperOption: { direction : 'vertical', height : window.innerHeight, onTransitionStart: function (swiper){ this.isHideIcon = swiper.activeIndex <= 3; }.bind(this) }
2、在拖拽的过程中,我还发现另一个bug。有时候拖拽边缘。会导致没有很好的弹性滚动,而是像普通页面一样滑动导致错误了。后来检查才发现,是因为你手势滑动的区域不是
39、默认Vue-cli脚手架的工程,npm run build之后的工程是必须部署在服务器根目录中的,只能用类似localhost:8080/#/来访问。原因是资源的路径读取都是以根目录'/',所以我们只需要修改静态资源的路径即可。打开工程目录中
/config/index.js
找到build属性中的assetsPublicPath,默认是“/” 修改为"./"即可
38、Error: [vuex] vuex requires a Promise polyfill in this browser. 与 babel-polyfill 的问题
事实上之前已经解决了。采用最笨重的解决方案就是npm install babel-polyfill 然后在webpack中如此设置:
entry: { 'babel-polyfill': 'babel-polyfill', app: './src/main.js' },
但在开发环境下,我们在IE11打开的时候依然有问题(但在现代浏览器中浏览居然没问题)。打开源码,可以看到是app.js先加载,然后才加载babel-polyfill. 其实在生产环境下(webpack编译之后)。可以正常运行。这是因为在webpack.prod.conf.js中的HtmlWebpackPlugin加入了 hunksSortMode: 'dependency' 属性。所以我们只要依样画葫芦。在webpack.dev.conf.js中找到HtmlWebpackPlugin加入了 hunksSortMode: 'dependency' 属性即可
37、(深度更新)好吧,其实36的理解是错误的,虽然解决了问题。但对于this.$set的理解是大错特错的。关键在于对vue的双向数据绑定太信赖而导致无视了原理。
https://segmentfault.com/a/1190000007787941?_ea=1459649
事实上,回忆一下我们在使用Vue的双向数据绑定的时候,我们首先需要在Data中配置属性,然后再绑定到template中。然后改变Data时,就会改变template。
但有没有经历过我这种情况,默认的Data的某个属性是空的,如 list: {} 我需要进行异步请求或者延迟操作之后,再给它赋值 list.fuck = 'fuck-Vue' 如果是这样的情况。你觉得template会更新么。
答案是否定的。这其实非常容易猜想到原因。Vue默认只会绑定Data中的数据,而你异步更新的Data。实质上只是更新了,但并没有绑定,所以template自然没有更新。那有没有解决方案呢?
有的。36的this.$set就是解决方案,它的作用是,添加绑定一个状态并且立刻更新template。也就是说,拿我的例子做说明,默认list: {} 当赋值的时候不再是单纯的this.list.fuck = 'fuck-Vue'; 而是 this.$set(this.list, 'fuck', 'fuck-vue');
这样即可解决,但需要注意一个问题: this.$set 不能添加或更新已有的属性,否则无效。什么意思呢?比如我的Data的list中,默认已经写有fuck这个属性了。如果你继续使用 this.$set(this.list, 'fuck', 'fuck-vue'); 则是无效的。
36、史诗级的知识点以及解决方案:一个复杂对象的变化,vue是不能监听到的,所以视图也不会随着改变?怎么办?
比如我有一个状态:
[ {toggle: true, rotate: true}, {toggle: false, rotate: false}, {toggle: false, rotate: false} ]
当然这个状态是通过api获取的。通过v-for渲染后,当我改变数组中其中一个对象的一个属性时:
this.Model[index].rotate= !this.Model[index].rotate
对象确实有改变,但视图不会更新。为什么? 详情阅读:https://cn.vuejs.org/v2/guide/reactivity.html
总之你只要知道,默认Vue是不能检测属性更新来刷新视图的。只有this.$set的方法来解决:https://cn.vuejs.org/v2/api/#Vue-set
methods: { slideupList (index) { this.Model[index].rotate= !this.Model[index].rotate this.Model[index].toggle = !this.Model[index].toggle /** * 由于this.Model是对象数组,当其中一个对象变化的时候,vue不能监听到的,所以视图也不会刷新 * this.$set 这个方法主要用于避开 Vue 不能检测属性更新的限制。 * 但这个方法的使用需要注意几点: * - 我是对数组中的某个值(这个值是个对象),直接重新赋值的。 * 如果我这里直接对数组的某个对象中的某个属性赋值,那是不行的。 * https://cn.vuejs.org/v2/api/#Vue-set * http://www.cnblogs.com/zhuzhenwei918/p/6893496.html * */ this.$set(this.Model, index, this.Model[index]) } }
35、如何监听Vuex的state? 通过Computed + watch的组合
computed: { get_translateY() { return this.$store.state.translateY; } }, watch: { get_translateY (val) { window.clearTimeout(this.Timer); this.isHeadAdd = true; this.hide = window.setTimeout(() => { this.isHeadAdd = false; }, 1000) } }
34、vue-router的keep-alive缓存策略
- keep-alive
是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
# 第三方手册
http://www.jianshu.com/p/0b0222954483
# 官方手册
https://vuejs.org/v2/api/#keep-alive
注意include不能有空格,如
33、vuex中mapState的概念是什么?有点懵逼???
其实就是重命名而已,为了可以减少你输入的键盘,详细可以看看这篇博客:
http://blog.csdn.net/a641832648/article/details/62233213
32、就凭这个坑,我相信typescript会替换babel
Cannot set property requestCount of #
场景这样的:使用了多个export + import * as xxx 组合,但属性只读
# state.js // 异步请求的数量 export let fetchCount = 0 // translateX的监听器 export let translateX = 0 // translateY的监听器 export let translateY = 0
# index.js import * as state from './state.js' console.log(state) // 你会发现这些属性只有getter没有seter。说明是只读的
原因是babel的配置中 .babelrc的presets属性中存在{ "modules": false }。移除就好了。
31、Vuejs的动画API更新换代了N次。不得不说我越来越欣赏和熟悉了。这要归功于完美用户体验的API手册。
下面这个demo是演示 animate.css 与 自定义transition 和 vuejs的API结合
"app">"fuck"> class="div" v-if='isShow'>
30、可以使用@input 代替 watch
由于@input本身就是用来监听表单控件(input/select/radio等)的值变化,并且在@input事件的回调中,他的执行是比v-model被赋值快的,依赖这个特性可以代替watch的效果
test (e) { // 你会发现不一样v-model此时还没赋值 console.log(this.test_model, e.target.value) }
29、深度监听 deep watch
当使用watch时,如果要监听的是一个对象或者一个数组。默认是监听不了的。需要开启deep
http://cn.vuejs.org/v2/api/#watch
http://www.cnblogs.com/hity-tt/p/6677753.html
data () { return { // 表单数据集 formModel: { // 借款类型 borrow_type: '', // 借款金额 borrow_money: '', // 业务状态 status: '', // 备注 remark: '' } } }, watch: { formModel: { handler (newValue, oldValue) { console.log(newValue) }, deep: true } }
28、安卓和IOS时间转换和显示的兼容性问题,统一解决
// 时间补0辅助函数 const padNumber = (num, fill) => { //改自:http://blog.csdn.net/aimingoo/article/details/4492592 var len = ('' + num).length; return (Array( fill > len ? fill - len + 1 || 0 : 0 ).join(0) + num); } // 转化时间格式为年月日 const timeYMD = time => { // 如果传入非法参数,直接返回空 if (isNullOrEmpty(time)) return null // 兼容安卓的恶心情况 2017-05-2021:53:13 或 2017-04-1000:00:00 替换为: 2017-05-20 21:53:13 if (/(-|\/){1}(\d{4})/.test(time)) { // 获取中点 const mid = time.lastIndexOf('-') + 3 // 生成正确的时间字符串 time = time.substr(0, mid) + ' ' + time.substr(mid) } // 兼容IOS和安卓 var d = new Date(Date.parse(time.replace(/-/g, "/"))) // 如果转换成功 if (d != 'Invalid Date') { time = d.getFullYear() + "-" + padNumber(d.getMonth() + 1, 2) + "-" + padNumber(d.getDate(), 2) + ' ' // 如果时都不为0的时候,才叠加上 if (d.getHours()) { time += d.getHours() + ':' // 如果时分秒都不为0的时候,才叠加上 time += padNumber(d.getMinutes(), 2) + ':' // 如果时分秒都不为0的时候,才叠加上 time += padNumber(d.getSeconds(), 2) } // 返回转换成功后的值 return time } // 否则返回转换失败的标记 return 'Invalid Date' }
27、必须掌握的技巧:webpack 与 路由 —— 代码分割
https://router.vuejs.org/zh-cn/advanced/lazy-loading.html
http://www.css88.com/doc/webpack2/guides/code-splitting-require/
# API地址:http://www.css88.com/doc/webpack2/guides/code-splitting-require/ # webpack 在编译时,会静态地解析代码中的 require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 通过 jsonp 来按需加载。 # router.js const Home = r => require.ensure([], () => r(require('../views/Home.vue')), 'Home') # vue-cli脚手架中/build/webpack.prod.conf.js,output修改为以下样子: output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[name].[chunkhash].js') }
26、组件单元测试所需要的知识点:
# ./my-component.js export default { template: '{{ fuck }}', props: ['fuck'], created () { console.log('created') } } # main.js // 制作组件 Vue.component('MyComponent', Object.assign(MyComponent, { data () { return { fuck : 'you' } }, beforeMount () { console.log("beforeMount") } })); // 新建组件 使用new Vue({//...}) 或者 Vue.extend({ //... }) 都可以 const HtmlContainer = Vue.extend({ data () { return { text: '喵了个咪' } }, template: `` }) // 组件需要挂载,有两种方式 // 方式1: const vm = new HtmlContainer({ el: document.createElement('div'), }) // 方式2: const vm = new HtmlContainer().$mount(document.createElement('div')) // 打印来看看结果吧: console.log(vm, vm.$el, vm.$el.textContent)
25、路由可选参数的坑:
# 如果该参数是动态的数值,直接加入‘?’即可 /houseBusinessDetails/:id? # 但如果是固定的数值,则需要加入()?才可以 /houseBusinessDetails/(id)?/:id?
# 正则表达式加可选参数
/myBusiness/:tag([0-2]?)
24、低端机的es6/7兼容性问题:[vuex] vuex requires a Promise polyfill in this browser.
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转码。
举例来说,ES6在Array
对象上新增了Array.from
方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill
,为当前环境提供一个垫片。
知乎关于babel-runtime的问题:https://www.zhihu.com/question/34854349
阮一峰的babel和es6相关文章:http://www.ruanyifeng.com/blog/2016/01/babel.html
# 安装 npm install --save-dev babel-polyfill # webpack entry: { 'babel-polyfill': 'babel-polyfill', app: './src/main.js' }
Babel默认不转码的API非常多,详细清单可以查看babel-plugin-transform-runtime
模块的definitions.js文件。
这样一来就可以使用所有的es6/7特性了。该方案可以说是对方案【14】的无脑版。无脑引入所有的特性。非常冗余但没办法的时候可以用
23、常用的组件内的路由钩子
beforeRouteEnter (to, from, next) { next(vm => { if (from.path === '/myBusiness') vm.$router.push('/') next() }) }
22、从vuex、store的一个使用细节感受到vuejs的开发哲学:
我一直以为store的依赖vue的。直到有一次,意外的先执行了store中自定义的action,然后再初始化new vue({store})。却依然正常无误后才理解。原因store打从一开始就不需要依赖vue,它之所以需要加入到vue,只是为了可以在vue的生命周期中,方便使用this来调用它仅此而已。store完全可以单独使用。从这点可以看出,vue的插件系统和设计非常的优秀
21、fastclick的bug以及解决方案: 当点击select的时候,浏览器会渲染出源生的列表,这时候,表单很可能向上拖拉。这时候,鼠标会继续渗透,很可能刚好点击到拖拉后的input上。然后会呼出键盘,取消掉select。解决方案如下:
20、史诗毁灭宇宙级别的坑:webpack1 版本的 autoprefixer 无法在build编译后生效,只在开发模式下生效,原因是webpack.optimize.UglifyJsPlugin 插件的影响,注释掉即可
19、使用new Date()的一点兼容性小坑
# 并不是所有的浏览器都支持以下转换语法 let data = new Date('2017-04-30 16:47:09') # 但都支持这种转换方法 let data = new Date('2017/04/30 16:47:09') # 所以尽可能要对 ‘-’ 进行替换 var date = new Date(this.model.Model.claimDate.replace(/-/g, '/'));
18、放置冒泡和鼠标穿透最简单的方法:@click.stop
17、过滤器的正确用法和常规用法
无代码演示,直接使用computed即可。聪明的你肯定知道怎么做了。
随便找的一个例子:https://blog.csdn.net/sinat_17775997/article/details/56495373
简单示例:
"text" v-model="search_content">
- class='list__items'>
- for='(item, index) in filterItems' :key='index' @click='handleClick(item.schoolId)'> {{ item.schoolName }}
data: { search_content: '', // 学校列表 items: [] },
computed: { filterItems: function () { var self = this; return self.items.filter(function (item) { return ~item.schoolName.indexOf(self.search_content.trim()) }) } },
16、v-html 渲染html的内容
class="notice-body" v-html="myData['notice_content']">
15、$nextTick + Mounted 解决对未渲染的dom的进行操作报错的问题
methods: { changeSwiperAction () { this.$nextTick(() => { for (let [index, elem] of this.swpierList.entries()) { if( elem.title === this.swiperAction ) return this.$refs.swiperRef.swiper.slideTo(index, 300, false) } }) } }, mounted () { this.changeSwiperAction() }
-14、es2016/es6/es7以上的js尽管有babel编译,或者什么babel的plugin的transform-runtime也没什么用。当你使用了新特性的时候,依然不会进行转换,依然需要修补匠babel-polyfill
但请按需引入,缺什么就补什么:https://github.com/zloirock/core-js
或者使用npm install --save-dev babel-polyfill安装,然后在node_modules中查找core-js文件夹也可以
// 按需加载Es修补匠 require('./modules/es7.object.entries') require('./modules/es6.array.iterator');
-13、同一个坑我踩了两次:双向数据绑定
我清空两个数组的数据,为了简洁这样写:
this.messageList = this.businessList = []
但这样一来,根据vuejs的特性,这两个list就绑定上了。数据就会同步了……(mmp),所以改为这样就好了……(cnm)
// 清空数据 this.messageList = [] this.businessList = []
-12、组件 vue-awesome-swiper 的坑
监听切换事件,但如果使用 onSlideChangeStart、onSlideChangeEnd、onSlideNextStart、onSlidePrevStart 等方法。完全不可以监听,应该说监听很困难,经常出错。事实上应该使用onTransitionEnd、onTransitionStart
当动画完成时触发:
swiperOption: { pagination: '.swiper-pagination', paginationClickable: true, nextButton: '.swiper-button-next', prevButton: '.swiper-button-prev', spaceBetween: 30, effect: 'coverflow', onTransitionStart: function (swiper) { this.drawMap.index = swiper.activeIndex }.bind(this) }
-11、主动更新组件上的model
当我们开发了一个子组件,并且给某个地方的父组件调用了。此时,如果父组件在子组件上绑定了一个V-model。父组件希望子组件的某些状态变化时,可以顺便更新一下这个v-model的值。那么问题来了。子组件获取和更新这个未知的v-model呢?
# 获取v-model console.log(this.value); // 对,就是这么简单 # 更新组件上的v-model值. 只需要调用input事件即可,参数为更新的值 fuckXXOO (v) { this.$emit('input', v); }
-10、 属性函数传参的尴尬
如果要为组件绑定一个click事件,可以使用@click="fuck($event)"。 这是正常的。
但如果是自定义组件,你的@click可能会无效。这时我们想可以传递一个函数进去,让组件本身@click处理。那好,我传递一个属性:clickMethod="fuck('123')"。那么就不正常了。因为该函数fuck('123')会立即执行。根据这个特性,正确的解决方案是这样的:(返回一个函数)
# template :clickMethod="MenuListClick(item.title,index,item.path)" # script MenuListClick (title,index,to) { return function () { console.log(title,index,to) } }
-9、双向数据 与 组件,以自定义select组件为例。原理很简单,子组件使用$emit来调用绑定在函数组件上的函数,并且传参。而绑定在组件上的函数是在父组件中运行的。拿到参数后父组件就可以为所欲为了
# 子组件中的模板 <select class="mint-field-core" v-if="type === 'select'" @change="$emit('fuck', currentValue)" v-model="currentValue"> select> # 子组件中的属性和状态 data() { return { currentValue: this.value }; }, props: { option: { type:Array }, value: {} } # 父组件中使用子组件: # template模板"业务类型" placeholder="请选择业务类型" type='select' :option="list" :value="currSelect" @fuck="handleChange"> # data状态 list:[ {"value":"1","text":"车易贷1"}, {"value":"2","text":"车易贷2"}, {"value":"3","text":"车易贷3"}, {"value":"4","text":"车易贷4"} ], currSelect:"1" # methods方法 handleChange (val) { console.log("handleChange",val); this.currSelect = val }
-8、v-model的新姿势:任何组件都可以使用
以前以为只有表单控件才可以拥有v-model的权利和意义,
事实上任何元素/组件(包括自定义组件),都可以使用v-model。
比如说我定义了一个
# template <lee-select v-model="list"> # script data () { return { list:[ {"id":"1","text":"车易贷1"}, {"id":"2","text":"车易贷2"}, {"id":"3","text":"车易贷3"}, {"id":"4","text":"车易贷4"} ] } }
那么我在lee-select组件的内部如何获取该v-model并且循环呢?事实上,系统内置了一个状态:【value】
代码如下:
<option v-for="v in value" value='v'>{{ v.text }}option>
这样一来,就正常的获取并且遍历出来拉!!!
-7、$refs的简单使用:获取Dom对象
class="mint-loadmore-top" ref="loadImage">// 查看各种各样的dom属性 console.log(this.$refs.loadImage);
-6、watch的简单似乎用
data() { return { translate: 0 } } watch: { translate (curVal, oldVal) { this.Remtranslate = curVal / 75 } }
-5、让路由与状态融合的插件:vuex-router-sync
import { sync } from 'vuex-router-sync' import router from './router' sync(store, router)
把 vue-router
的狀態放進 vuex
的 state
中,這樣就可以透過改變 state
來進行路由的一些操作
import * as type from './mutation-types.js' const mutations = { [type.IS_LOADING] (state, b) { console.log(state.route) // 打印出路由的实例,那么我们就可以通过它来改变路由地址,为所欲为了 state.is_loading = b; } } export default mutations;
-4、props 设置默认值和其他配置
props: { show: { required: false, default: true } }
-3、 v-bind 和 v-model的区别
我们知道两者的作用都是可以实现数据的双向绑定。
v-model只能用于表单控件上,比如input、textarea、radio比如input、textarea、 select/option等,并且主要是来获取和操作表单控件的Value。
这样区分的话,v-bind的作用就很明显了。它可以用来绑定任何属性。比如class / id / placeholder ,甚至自定义属性:fuck / shit 等。但请不要尝试代替v-model的职能,比如绑定了input控件的value属性。那么显然是无效的。还请用V-model绑定
-2、 npm run build 之后路径的坑:
默认的脚手架,编译之后所有的静态资源路径是相对于/static。 事实上应该是./static 那应该如何改呢?
打开脚手架目录下 /config/index.js 文件。
修改 build -> assetsPublicPath : "./" 即可
-1、神奇的computed,又叫属性计算,其作用等于加强版的$watch。它的神奇之处在于可以自动监听函数中使用的状态。如下demo中,可以自动监听 firstName 和 lastName,当它们两个变化的时候,自动会执行 fullName 这个函数,然后更新view层
如果你使用$watch的话,还需要同时编写对 firstName 和 lastName 的回调函数
{{fullName}}var vm = new Vue({ data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
0、webpack编译vue不通过的情况(比如Cannot find modulee test.vue,但是可以找到js文件,很明显就是vue文件编译失败):
# 检查.babelrc 或 babel-loader 的配置,确保使用了stage-0以上的版本 npm install babel-preset-stage-2 --save-dev
1、