赞
踩
vue官网称vue.js是渐进式的JavaScript框架,但什么是渐进式?什么是框架呢?
在项目的开发中,起初是一个简单的demo,使用vue的基础知识就足够了。
但随着项目的开发,页面会越来越多,这时就需要使用vue-router(路由)来管理页面,从而实现组件化开发。
后期数据会逐渐增多,这时就可以使用vuex(状态管理工具)来管理数据。
库(Lib):一系列函数的集合,需要实现某个功能,由开发人员去调取。类似于自己购买配件,需要自己去组装成一台台式电脑之后才能使用。
框架(Framework):一套完整的解决方案,框架中制定了一套规则,使用框架的时候,只需要按照规则,把代码放到合适的地方,然后框架会在合适的时机,主动调用开发人员的代码。类似于购买了笔记本电脑,只需要按照它给定的方式去使用。
二者核心的区别在于:控制反转。库的控制权在开发人员,但框架的控制权在于框架。
M:model(数据层)专门用于操作数据
V:view(视图层)相对于前端来说就是页面
C:controller(控制层)是数据层与视图层沟通的桥梁,用于处理逻辑业务
M:model(数据层)
V:view(视图层)
VM:view model(视图模型)
核心 : M <===> VM <===> V
vue的数据双向绑定(v-model) 是通过 数据劫持
Object.defineProperty() 来实现的,因此本质上vue是单向数据流
。
注意 :Object.defineProperty() 是 es5 提出来的一个 无法 shim(兼容) 的特性 , 无法兼容ie8以下。
<input type="text" id="txt" />
let obj = {} let temp var txt = document.querySelector("#txt") //V→M txt.oninput =>{ obj.name = this.value } // M→V // 参数1 : 要给哪个对象设置属性 // 参数2 : 给对象设置什么属性 // 参数3 : 属性的修饰符 Object.defineProperty(obj,'name',{ set(newVal){ temp = newVal txt.value = newVal }, get(){ return temp } })
指令是HTML标签上特殊的属性,添加额外的功能。vue的指令统一是‘v-’开头
作用:实现数据的双向绑定。
使用场景:只能用于表单元素(文本框、单选框、多选框、文本域、下拉框等)
注意:使用在不同的表单元素,绑定的类型也不一样。例如:文本框是文本内容,单选框绑定的是状态(false/true)
作用:展示数据
用法类似于innerText与innerHtml,v-text不识别标签,v-html识别标签。与插值表达式实现的效果一样。
作用:动态显示一个属性值
写法:
<!-- 全写 -->
<div v-bind:title="msg">嗯哼</div>
<!-- 简写 -->
<div :title="msg">嗯哼</div>
运用
<style> .blue{color:blue} .fz{font-size:24px} </style> <div id="app"> <div :class="{blue:isBlue,fz:isFz}">嗯哼</div> </div> <script> const vm = new Vue({ el:"#app", data:{ isBlue:true, isFz:false } }) </script>
v-model | v-bind | |
---|---|---|
写法 | <input type="checkbox" v-model="isChecked1" /> | <input type="checkbox" v-bind:checked="isChecked2" /> |
使用场景 | 表单元素 | 任何元素的属性 |
方向 | 双向数据绑定 | 单向M→V |
作用:注册/绑定事件
写法:
<!-- 全写 -->
<button v-on:click="fn">嗯哼</button>
<!-- 简写 -->
<button @click="fn">嗯哼</button>
<button @click="fn($event,123)">带参数时需获取事件对象</button>
官方地址
格式:@事件.事件修饰符 = “事件函数”
范围:修饰任意事件
.prevent | .stop | .capture | .once | .self | .passive | |
---|---|---|---|---|---|---|
功能 | 阻止默认行为 | 阻止冒泡(添加在子元素) | 捕获(添加在父元素) | 只触发一次(添加在子元素) | 只有点击自己才触发(关闭冒泡和捕获) | 用于移动端提高性能,只需判断一次是捕获还是冒泡 |
官方地址
格式:@事件.按键修饰符 = “事件函数”
范围:修饰按键事件
.enter | .up | .down | .left | .right | .esc | delete | |
---|---|---|---|---|---|---|---|
功能 | 回车 | 上 | 下 | 左 | 右 | esc键 | 删除 |
作用:遍历数据,根据数据里元素的个数创建对应个数的标签
注意
:
<!-- 遍历数组 -->
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
<!-- 遍历对象 -->
<ul>
<li v-for="(value,key,index) in obj" :key="index">{{key}}:{{value}}</li>
</ul>
<!-- 遍历数组对象 -->
<ul>
<li v-for="item in arrObj" :key="item.id">{{item.name}}</li>
</ul>
作用:控制元素的显示与隐藏
区别:控制方式不同
v-show | v-if | |
---|---|---|
显示 | display:block | 新建节点 |
隐藏 | display:none | 删除节点 |
因为v-if操作的是节点,因此比较消耗性能 ,操作频率高的情况下,不推荐使用v-if
包含v-if、v-else-if、v-else,用法与if(){}else if (){}else(){}类似
<div id="app">
<p v-if="age >= 18">成年人</p>
<p v-else-if="age >= 12">青少年</p>
<p v-else="age < 12">儿童</p>
</div>
v-pre:不解析,可直接显示 Mustache 标签即“{{ }}”符号
v-once :解析一次
v-cloak :遮盖,可以隐藏未编译的 Mustache 标签直到实例准备完毕。
<!-- v-cloak使用步骤-->
<!-- 1. 添加指令:-->
<div v-cloak>{{ num}}</div>
<!-- 2.利用属性选择器添加样式 -->
<style>
[v-cloak]{
display:none
}
</style>
<!--3. vue会在数据解析完,自动删除v-cloak指令-->
<!-- 计算属性的案例 --> <div id="app"> num1 : <input type="text" v-model="num1" /> + num2 :<input type="text" v-model="num2" /> = <span>{{ sum }}</span> </div> <script> const vm = new Vue({ el: '#app', data: { num1: '', num2: '' }, computed: { sum() { return +this.num1 + +this.num2 } } }) </script>
DOM异步更新,若需获取DOM更新后的数据就需要用setTimeOut(延时器)或者 $nextTick 。但延时器的时间不好把控,因此需要使用 $nextTick。
this. $nextTick()在DOM异步更新结束之后,自动执行回调函数,可在回调函数中获取自己所需的数据。
<div id="app"> <p>{{ num }}</p> <button @click="fn">按钮</button> </div> <script> const vm = new Vue({ el: '#app', data: { num: 100 }, methods: { fn() { console.log(document.querySelector('p').innerText) this.num += 1 // 方式1 : 延时器 // setTimeout(() => { // console.log(document.querySelector('h1').innerText) // }, 1000) // 方式2 : $nextTick,DOM更新完毕后,就可以获取DOM数据了 this.$nextTick(() => { console.log(document.querySelector('p').innerText) }) } } }) </script>
data中存在的数据,vue可监测到,在进行修改时,数据能响应式改变;但data中不存在的数据,数据就不会响应式变化。如果想让data中不存在的数据进行响应式更新,就需要借助于 $set
格式:this. $set(对象,属性,初始值)
this.$set(this.obj,'name','stitch')
作用:监听数据的变化,可应用于本地持久化
使用:
//简单类型 watch:{ //参数1:新值 //参数2:旧值(可省略) 被监听数据(newVal,oldVal){ console.log(newVal) } } //复杂类型(复杂类型监听到的是地址,地址一直未改变,因此监听数据的变化需要深度监听) //方式1:深度监听 watch:{ obj:{ //深度监听 deep:true, //立即监听(非必要) immediate:true, //处理函数 handler(newVal,oldVal){ console.log(newVal.name) } } } //方式2:直接监听需要的属性 watch:{ 'obj.name'(newVal){ console.log(newVal) } }
computed是根据一个或多个值(data里的值),通过计算得到一个新值,当data的值发生变化得到的新值随着改变,是多对一或者一对一的关系。别人影响我。
watch监听的是data里的数据,当数据改变时,影响接下来的操作,是一对多的关系。自己改变影响别人。
作用:vue.js允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示。
步骤:
参数1:过滤器名称
参数2:回调函数(参数1:需格式化的数据,参数2(可省略):过滤器传过来的参数)
注意:回调函数一定要有返回值
Vue.filter('filterName', function (value) {
return value的处理
})
new Vue({
el:'#app',
data:{
date: new Date()
}
})
new Vue({
el:'#app',
data:{
date: new Date()
}
filters: {
filterName(value) {
return value的处理
}
}
})
<p> {{ date | filterName }} </p>
<div v-bind:id="rawId | formatId"></div>
概念: 生命周期函数又称为钩子函数
注意: 钩子函数到对应阶段时自动调用;命名是规定好的
钩子函数 | 阶段 | 使用场景 |
---|---|---|
beforeCreate | 挂载阶段 初始化内部使用的事件,开启生命周期 | 无法获取data中的数据;加loading事件 |
created★ | 挂载阶段 数据响应式之后 | 发送Ajax请求;操作data中的数据;获取本地存储数据 |
beforeMount | 挂载阶段 DOM渲染前,数据与模板初始化完成 | \ |
mounted ★ | 挂载阶段 DOM渲染完成后 | 操作DOM元素;发送Ajax请求 |
beforeUpdate | 更新阶段 更新前 | 获取更新前的数据 |
updated ☆ | 更新阶段 数据更新完成后 | 获取更新后的数据 |
beforeDestory | 卸载阶段 | \ |
destoryed | 卸载阶段 | 清理开启的定时器;移除手动创建的DOM对象等 |
概念:可复用的UI模块;本质上是一个Vue实例。
注册:
<!-- 全局注册 --> Vue.component('child',{ template:`<div> child </div>`, data(){ return{ } } }) <!-- 局部注册 方式1--> const vm = new Vue({ components : { child : { template : `<div>child</div>` } } }) <!-- 局部注册 方式2 --> const child = { template : `<div></div>` } const vm = new Vue({ components : child })
注意:
1. 注册组件需要在vue实例前
2. template只有一个根组件
3. 配置对象和vue实例中大部分一样
4. 组件中的data是一个函数,函数里面返回一个对象是组件里的数据,因为我们希望组件被复用,但不希望组件中的数据被复用
组件化开发
概念:将一个页面抽象成一个个独立的组件。
优点:可复用性强
与模块化开发的区别:模块化开发重业务,重逻辑,重功能,而组件化开发重界面/UI。
原因:组件是一个封闭的个体,组件之间无法直接获取对方的数据,因此需要组件之间的通信机制来访问数据。
实现思路:子组件通过配置项props,自定义一个属性,父组件通过这个属性将数据传递给子组件。
<div id="app"> <!-- 1.通过属性,父组件将数据传递给子组件 --> <child :msg="pmsg"></child> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> Vue.component('child',{ template:`<div id="child">子组件:{{msg}}</div>`, // 2.子组件通过配置项props,指定需接收来的数据 props:{ msg : String } }) const vm = new Vue({ el:'#app', data:{ pmsg:'父组件中的数据' } }) </script>
实现思路:子组件通过触发自定义事件,利用函数传参的方式,将数据传递给父组件。
<div id="app"> <p>父组件:{{cmsg}}</p> <!-- 2.在子组件上定义一个事件 --> <child @event="pfn"></child> </div> <script> Vue.component('child',{ template:`<div id="child"></div>`, data(){ return{ cmsg:'子组件中的数据' } }, created(){ // 3.触发自定义事件 this.$emit('event',this.cmsg) } }) const vm = new Vue({ el:'#app', data:{ cmsg:'' }, methods:{ // 1.在父组件中准备好一个方法 pfn(cmsg) { this.cmsg = cmsg } } }) </script>
vm.$emit( event, arg )
:触发当前实例上的事件,(参数1:事件名,参数2:传递的参数)
实现思路:通过事件总线(event bus)为媒介,发送数据的组件触发事件,接收数据的组件注册事件
<div id="app"> <child1></child1> <child2></child2> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script> // 1.创建事件总线 const bus = new Vue() Vue.component('child1',{ template:`<div id="child">child1:<button @click='send'>发送数据</button></div>`, data(){ return{ msg1:'child1发送的数据' } }, methods:{ send(){ // 2.发送数据的组件,触发事件 bus.$emit('transferData',this.msg1) } } }) Vue.component('child2',{ template:`<div id="child">child2:{{msg2}}</div>`, data(){ return{ msg2:'' } }, created(){ // 3.接收数据的组件,注册事件 bus.$on('transferData',(res)=>{ this.msg2 = res }) } }) const vm = new Vue({ el:'#app' }) </script>
注意: 要保证注册事件的代码先执行,触发事件的代码后执行
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
大小写问题
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
<script>
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
</script>
prop校验
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
props: { // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) propA: Number, // 多个可能的类型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 带有默认值的数字 propD: { type: Number, default: 100 }, // 带有默认值的对象 propE: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }
作用:获取标签/组件
先注册ref,凡是注册过ref的标签/数组,都会被refs收集起来,refs是一个键值对(键:ref值; 值:注册refs的组件或元素)。
<div id="app"> <!-- 1.注册 --> <child ref='c'></child> </div> <script> Vue.component('child',{ template:`<div id="child"></div>`, data(){ return{ cmsg:'子组件中的数据' } } }) const vm = new Vue({ el:'#app', mounted(){ //2.使用 //父组件可获取到子组件 console.log(this.$refs.c) //父组件可获取到子组件中的信息 console.log(this.$refs.c.cmsg) } }) </script>
注意: 若想一进来就要获取$refs的值,最早也要等DOM元素渲染之后,即mounted中。
学习目的:为了更好管理单页面应用程序(SPA)
路由是浏览器URL中的哈希值,与展示视图内容之间的对应规则。
vue中的路由是由hash和component的对应关系,一个哈希值对应一个组件。
准备工作 (3个)
安装 : npm i vue-router
引入 :
注意: 引入路由一定要在引入vue之后,因为vue-router是基于vue工作的
<script src="./vue.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
实例路由对象 + 挂载到vue上
实例路由对象 : const router = new VueRouter()
挂载到vue上 : new Vue({ router,data,methods })
验证路由是否挂载成功, 就看打开页面,最后面有没有个 #/
具体步骤
1). 手动:在地址栏手动输入(测试用)
2). 声明式导航: <router-link to="/one"></router-link>
3). 编程式导航:
跳转:this.$router.push('/one') 有记录,可返回
返回:this.$router.back()
替换:this.$router.replace('/one') 没有记录,不可返回(返回到根目录,常用于支付)
// path : 路由路径
// component : 将来要展示的路由组件
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
<router-view />
一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息
一个哈希值路径 对应 一个路由对象
$route.path★
string
"/detail/4"
。# 后面?前面的内容
$route.params★
Object
$route.name★
string
$route.meta 元信息★
Object
$route.query
Object
/4?age=21
,则有 $route.query.age == 21
,如果没有查询参数,则是个空对象。$route.hash
类型: string
当前路由的 hash 值 (带 #
) ,如果没有 hash 值,则为空字符串。
$route.fullPath
string
# 演示 :
<router-link to="/detail/4?age=21#one">detail</router-link>
{ path: '/detail/:id?', component: detail ,name='detail',meta:{title:'stitch'}}
在组件内 created打印 this.$route
> fullPath: "/detail/4?id=001#one"
> hash : "#one"
> params : {id:'4'}
> query : {age : 21}
> path : '/detail/4'
> name:'detail'
> meta:{title:'stitch'}
作用:根据不同的条件动态的去配置路由规则。在vue-router的路由路径中,可以使用动态路径参数给路径动态的传值。
动态路由在来回切换时,由于它们都是指向同一组件,Vue不会销毁再重新创建这个组件,而是复用这个组件。
<div id="app"> <!-- 1.入口 --> <router-link to="/detail/1">手机1</router-link> <router-link to="/detail/2">手机2</router-link> <router-link to="/detail/3">手机3</router-link> <!-- 4.出口 --> <router-view ></router-view> </div> <script src="../node_modules/vue/dist/vue.js"></script> <script src="../node_modules/vue-router/dist/vue-router.js"></script> <script > //3.组件 //获取参数的三种方式 const detail = { // 方式1 : 组件中直接读取 template:`<div>详情页信息:{{$route.params.id}}</div>`, // 方式2 : js直接读取 created(){ // 打印只会打印一次,因为组件是复用的,每次进来钩子函数只会执行一次 console.log(this.$route.params.id) }, // 方式3 : 监听路由的参数。为什么不需要深度监听,因为一个路径变化,就会对应一个对新的路由对象(地址变) watch:{ $route(to,from){ console.log(to.params.id) } } } const router = new VueRouter({ //2.匹配规则 routes : [ {path:'/detail/:id?',component:detail} ] }) const vm = new Vue({ el:'#app', router, components :{detail} }) </script>
作用:嵌套路由就是在路由里面嵌套它的子路由,简而言之,就是让一个组件显示在另一个组件中。
const Parent = { //1.在父组件中留一个出口 template:`<div>Parent <router-view /></div>` } const Child = { template:`<div>Child</div>` } const router = new VueRouter({ routes:[{ path:'/parent', component:Parent, //2.子组件的路由的路由规则需放到父组件的children中 /** * path:'/child' =>入口:'/child' * path:'child' =>入口:'/parent/child' */ // children:[{path:'/child',component:Child}] children:[{path:'child',component:Child}] }] })
作用:在创建Router实例的时候,在 routes中给某个路由设置名称name值作为标识,然后就可以通过这个名称来代替path路径去指向某个路由组件
<!-- 使用 -->
<router-link :to="{name:'one'}">One</router-link>
//命名name
routes:[
{path:'/one',name:'one',component:One}
]
作用:想同时或同级在一个页面中展示多个视图,而不是嵌套展示,则可以在页面中定义多个单独命名的视图
<router-view />
没添加name时,显示默认视图routes:[
{
//1.配置名字
path:'/',components:{
h:Header,
m:Main,
//指定默认显示视图
default:Footer
}
}
]
<!-- 2.给每个出口指定名字 -->
<router-view name='h'></router-view>
<router-view name='m'></router-view>
<!-- 未指定name时,使用default(默认)视图 -->
<router-view ></router-view>
const router = new VueRouter({ routes: [ //方式1:路径 { path: '/', redirect: '/one' }, //方式2:name { path: '/', redirect: { name: 'one' }}, //方式3:函数 //to:目标路由对象 { path: '/', redirect: to => { //return '/two' //可根据路由中的条件,来决定重定向到哪 if(to.params.id ==1){ return {name:'one'} }else{ return {name:'two'} } }} ] })
const One = { //使用 template:`<div>组件One:{{id}}</div>`, //指定 props:['id','aa','bb'] } const router = new VueRouter({ routes:[ // 方式1:原始 {path:'/one/:id?',component:One} // 方式2:布尔模式 将路由参数id,作为组件one的属性存在 {path:'/one/:id?',component:One,props:true} // 方式3:对象模式 {path:'/one',component:One,props:{aa:'aaa'}} // 方式4:函数模式 {path:'/one',component:One,props:to=>{return {bb:'bbb'}}} ] })
导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。
to: 即将要进入的目标 路由对象
from: 当前导航正要离开的路由对象
next:1).允许访问:next() 2).不允许访问:next(false) 3).跳转到其它界面访问:next(‘login’)
const isLogin = false
router.beforeEach((to,from,next)=>{
if(to.name == 'login'){
next()
}else{
// isLogin ? next():next(false)
isLogin ? next():next('/login')
}
})
全局前置路由守卫:beforeEach((to,from,next)=>{ })
全局后置路由守卫:afterEach((to,from)=>{ })
独享路由守卫:beforeEnter((to,from,next)=>{ })
组件内路由守卫:beforeRouteEnter((to,from,next)=>{ }) / beforeRouteLeave((to,from,next)=>{ }) 通过路由规则触发才有
activated路由组件被激活时触发。
deactivated路由组件失活时触发。
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
项目名不能有汉字,不能取名叫 webpack
package.json
, 命令 : npm init -y
npm i -D webpack webpack-cli
webpack : webpack 工具的核心包
webpack-cli : 提供了一些在终端中使用的命令
-D(--save-dev) : 表示项目开发期间的依赖,也就是 : 线上代码中用不到这些包了
main.js
文件console.log('要被打包了');
package.json
的scripts
中,添加脚本// webpack 是webpack-cli 中提供的命令, 用来实现打包的
// ./main.js 入口文件,要打包哪个文件
"scripts": {
"build": "webpack main.js"
},
npm run build
mode
"build" : "webpack ./main.js --mode development"
如果没有设置 mode 配置项, webpack 会默认提供 生产环境(production);生产环境下, 打包生产的js文件都是压缩后的, 开发环境下代码一般是不压缩的
"build" : 入口 --output 出口
"build": "webpack ./src/js/main.js --output ./dist/bundle.js --mode development",
第一步 : 项目`根目录`下, 创建一个 `webpack.config.js`文件 (文件名固定死)
第二步 : 在 `webpack.config.js` 中,进行配置
* webpack 是基于 node的 , 所以配置文件符合 node 方式书写配置
* 注意 : 不要再这个文件中使用ES6的的模块化 import语法
* main.js里可以使用,是因为要通过webpack转化为es5的
* 而这个是webpack 的配置文件,它是要转化别人的,所以必须要通过
第三步 : 修改 `package.json` 中的 `scripts` , 脚本命令为: "build": "webpack"
第四步 : 执行命令 : `npm run build`
作用:
- 能够根据指定的模板文件 (index.html),自动生成一个新的 index.html,并且注入到dist文件夹下
- 能够自动引入js文件
安装: npm i html-webpack-plugin
配置 :
//第一步: 引入模块
const htmlWebpackPlugin = require('html-webpack-plugin')
//第二步: 配置
plugins: [
// 使用插件 指定模板
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html')
})
]
作用 : 为使用 webpack 打包提供一个服务器环境
- 自动为我们的项目创建一个服务器
- 自动打开浏览器
- 自动监视文件变化,自动刷新浏览器…
安装: npm i -D webpack-dev-server
配置:
方式 1 : 命令行配置
脚本 : "dev" : "webpack-dev-server --open --port 3001 --hot"
运行: npm run dev
自动打开: 添加 --open
指定端口号 : 添加 --port 3001
热更新: --hot
( 局部更新 )
方式 2 : 配置文件配置
// hot 不要写在配置文件里面,,不然的话还要配其他插件麻烦
"dev" : "webpack-dev-server --hot",
devServer : {
open : true,
port : 3001
}
const path = require('path') const htmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // 入口 entry: path.join(__dirname, './src/js/main.js'), // 出口 output: { // 出口目录 path: path.join(__dirname, './dist'), filename: 'bundle.js' }, // 开发模式 mode: 'development' // 插件 plugins: [ // 使用htmlWebpackPlugin插件 指定模板 new htmlWebpackPlugin({ template: path.join(__dirname, './src/index.html') }) ] // 使用webpack-dev-server插件 devServer : { open : true, port : 3001 } }
npm run dev
==> 不会打包的 ,只会把项目放到服务器里npm run build
对项目进行打包,生成dist文件1 执行 : `npm run build`
2 模拟本地服务器 : 安装 : `npm i -g http-server`
3 把dist文件里的内容放到服务器里即可, 直接运行`http-server`
webpack 只能处理 js 文件,非 js(css.less.图片.字体等)处理处理不了, 借助 loader 加载器
npm i -D style-loader css-loader
webpack.config.js
中,添加个新的配置项 module
// loader
module: {
rules: [
//1.处理 css
// 注意点 use执行loader 顺序 从右往左
// css-loader : 读取css文件内容,将其转化为一个模块
// style-loader :拿到模块, 创建一个style标签,插入页面中
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
npm i -D less-loader less style-loader css-loader
module->rules
{ test :/\.less$/, use : ['style-loader','css-loader','less-loader'] },
npm i -D url-loader file-loader
module->rules
// 处理图片
{ test : /\.(jpg|png)$/, use : ['url-loader'] },
**注意:**url-loader (推荐) 和 file-loader 二选一即可
url-loader 默认会将图片转化为 base64 编码格式, 目的:提高性能,减少请求次数。
file-loader 在处理图片时, 会对文件进行重命名 :
原始: background-image: url(…/images/1.jpg);
处理后: background-image: url(9c9690b56876ea9b4d092c0baef31bb3.jpg);
base64 编码格式的图片说明 :
设置配置:
//方式1 :
{ test : /\.(jpg|png)$/, use : ['url-loader?limit=57417'] },
//方式2 :
{
test: /\.(jpg|png)$/, use: [
{
loader: 'url-loader',
options: {
// 比57417这个小 => 转化为base64
// 大于等于这个57417值 => 不会转base64 内部调用 file-loader 加载图片
limit: 57417
}
}
]
}
npm i -D url-loader
// 4. 处理字体图标
{ test:/\.(svg|woff|woff2|ttf|eot)$/,use:'url-loader'}
var o = { ...obj }
在谷歌上可以,edge 就不可以npm i -D babel-core babel-loader@7
npm i -D babel-preset-env babel-preset-stage-2
babel-polyfill与babel-plugin-transform-runtime
也是做兼容处理的,以前都是用这个,兼容更早的 { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
.babelrc
{ "presets": [ "env", "stage-2" ], ----------- // 暂时不用 // 如果未来某一天真的用到了polify "plugins": [ ["transform-runtime", { "helpers": false, "polyfill": true, "regenerator": true, "moduleName": "babel-runtime" }]
后缀为 .vue 的文件
注意 : 单文件组件,无法直接在浏览器中使用,必须经过 webpack 这种打包工具,处理后,才能在浏览器中使用
<template>
<!-- html代码 -->
</template>
<style scoped>
/*scoped:加上之后当前组件内的样式只在当前组件生效*/
/* css代码 */
</style>
<script>
export default {
// js代码
}
</script>
因为 webpack 配置繁琐, 阻止一批想用 vue 但是不会 webpack 的开发人员,所以作者直接将所有 vue 项目中用到的配置全部帮你写好了,这样,就不需要开发人员再去配置基础 webpack 配置项了
vue版本 | 安装命令 | 初始化项目命令 |
---|---|---|
2.0 | npm i vue-cli -g | vue init webpack 项目名 |
3.0及以上 | npm i @vue/cli -g | vue create 项目名 /vue ui |
查看版本:vue -V
安装过程:
? Project name # 回车 (项目名称)
? Project description # 回车 (项目描述)
? Author # 回车 (作者)
? Vue build standalone # => 运行时+编译 => 详见下面的问题1
? Install vue-router? # Yes (是否添加路由)
? Use ESLint to lint your code? # Yes (是否使用Eslint)
? Pick an ESLint preset Standard # standard (使用eslint的什么标准)
? Set up unit tests # No
? Setup e2e tests with Nightwatch? # No
? Should we run `npm install` for you after the project has been created? # (recommended) npm
问题1 : 两种编译模式 和 @
参考 : vue.js => 安装 => 对不同构建本版本的解释
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
router/index.js =>
import HelloWorld from '@/components/HelloWorld'
import HelloWorld from 'C:/users/.../src/components/HelloWorld'
问题2 : ESLint
概念 : ESLint 是在 JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。
在 vscode等编辑工具 中, 可以提示语法错误
在许多方面,它和 JSLint、JSHint 相似,除了少数的例外:
如何使用 eslint ?
"vetur.format.defaultFormatterOptions": { "prettier": { "semi": false, //不需要分号 "singleQuote": true, //使用单引号 "wrap_attributes": "force-aligned" } }, // 从这里往下大家都给加上 "editor.formatOnSave": true, //#每次保存的时候自动格式化 "eslint.autoFixOnSave": true, // #每次保存的时候将代码按eslint格式进行修复 "eslint.validate": [ { "language": "html", "autoFix": true }, { "language": "vue", "autoFix": true }, { "language": "javascript", "autoFix": true } ], "prettier.eslintIntegration": true, // #让prettier使用eslint的代码格式进行校验 "prettier.semi": false, //#去掉代码结尾的分号 "prettier.singleQuote": true, //#使用带引号替代双引号 "javascript.format.insertSpaceBeforeFunctionParenthesis": true, "editor.formatOnType": true, //#让函数(名)和后面的括号之间加个空格 "vetur.format.defaultFormatter.html": "js-beautify-html", "vetur.format.defaultFormatter.js": "vscode-typescript"
关闭 Eslint :
参考 : config/index.js ==> 26行 : dev.useEslint
设置为false
重启项目: npm run dev
问题3 : vscode安装 格式化插件 Prettier
安装 vscode 插件 Prettier - Code formatter
功能1 : shift + alt + F => 格式化代码
功能2 : 配合 eslint : 见上一个问题的配置
问题4 : eslint 检测警告
`//eslint-disable-next-line` # 忽略下一行的警告 可以使用单行注释/多行注释,其他都是多行注释
`/*eslint-disable*/` # 忽略当前整个文件的警告 需放在文件最前面
`/* eslint-disable no-new */` # 忽略前面是new开头的警告
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm i yarn -g
yarn add vue
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
// 假设操作成功 resolve => then
// resolve('成功')
// 假设操作失败 reject => catch
reject('失败')
},0)
})
p.then(res=>{
console.log('then:',res)
}).catch(err=>{
console.log('catch:',err)
})
promise封装 异步读取多个文件
const fs = require('fs') function readFiles (filePath) { const p = new Promise((resolve, reject) => { fs.readFile(filePath, 'utf-8', (err, data) => { if (err) { reject('读取文件失败') } resolve(data) }) }) return p } readFiles('./a.txt') .then(res1 => { console.log(res1) return readFiles('./b.txt') }) .then(res2 => { console.log(res2) return readFiles('./c.txt') }) .then(res3 => { console.log(res3) })
功能 | 格式 | |
---|---|---|
async | 修饰一个内部有异步操作的函数 | async + 异步函数 |
await | 等待一个异步处理的结果值 | await+ 异步操作(promise类型) |
const fs = require('fs') function readFiles (filePath) { const p = new Promise((resolve, reject) => { fs.readFile(filePath, 'utf-8', (err, data) => { if (err) { reject("读取文件失败") } resolve(data) }) }) return p } async function fn () { try { let res1 = await readFiles('./a1.txt') console.log(res1) } catch (err) { console.log('读取失败了') } let res2 = await readFiles('./b.txt') console.log(res2) } fn()
Promise.resolve(value=>{}) :快速返回成功的数据
Promise.regect(reason=>{}):快速返回失败的数据
Promise.all(promises=>{ }) :返回一个新的promise,只有所有的promise都成功才成功,有一个失败的promise就失败
Promise.race(promises=>{ }) :第一个完成的promise的结果状态就是最终结果状态
pending: 未完成
resolved: 异步完成成功的结果
rejected: 异步完成失败的结果
promise是微任务,执行代码是先执行宏任务再执行微任务。script与timeout是宏任务,但timeout是下一次的宏任务。
状态管理工具
状态即数据, 状态管理就是管理组件中的data数据
Vuex 中的状态管理工具,采用了 集中式 方式统一管理项目中组件之间需要通讯的数据
npm i vuex
vuex
之前一定要先引入 vue
<script src="./node_modules/vuex/dist/vuex.js"></script>
const store = new Vuex.Store()
store.state.num
store.state.num = 300
store.state.count = 300
可以修改值 , 但是vuex 也有严格模式,strict : true,
mutations : {}
increament(state) { state.count = 20; }
store.commit('increament')
// 传参最好传一个对象,多个值查看方便
store.commit('increament', {
num: 400
})
// payload 载荷
increament(state, payload) {
state.count = payload.num
}
需求 : 有个h1显示数字的标题, 点击按钮累加数字
<h1>{{ $store.state.num }}</h1>
this.$store.commit('addNum')
addNum(state) { state.num++ }
this.$store.commit('方法名');
this.$store.dispatch('方法名');
import { mapGetters } from "vuex";
computed:{
...mapGetters(['方法名'])
}
methods:{
...mapMutations(['方法名'])
}
methods:{
...mapActions(['方法名'])
}
代码 :
// 演示跨域问题
/* eslint-disable */
import axios from 'axios';
axios.get('https://api.douban.com/v2/movie/in_theaters').then(res => {
console.log(res)
})
报错 :
Access to XMLHttpRequest at 'https://api.douban.com/v2/movie/in_theaters' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
报错原因
项目运行在 http://localhost:8080
// I Your application is running here: http://localhost:8080
发送ajax请求 : //域名是 https://api.douban.com/v2/movie/in_theaters
因此出现跨域问题
修改 config/index.js
配置文件
proxyTable: {
'/myapi': {
// 代理的目标服务器地址
// https://api.douban.com/v2/movie/in_theaters
// /myapi/movie/in_theaters
target: 'https://douban.uieee.com/v2/',
pathRewrite: { '^/myapi': '' }, // 替换的请求地址
// 设置https
secure: false,
changeOrigin: true // 允许跨域 (必须设置该项)
}
},
最终代码
// axios.get('https://api.douban.com/v2/movie/in_theaters').then(res => {
axios.get("http://localhost:8080/api/movie/in_theaters").then(res => {
console.log(res);
});
最终配置 cli2.x :
proxyTable: {
'/myapi': {
// 代理的目标服务器地址
// https://api.douban.com/v2/movie/in_theaters
// /myapi/movie/in_theaters
target: 'https://douban.uieee.com/v2/',
pathRewrite: { '^/myapi': '' },
// 设置https
secure: false,
// 必须设置该项
changeOrigin: true
}
},
最终配置 3.X
vue.config.js
module.exports = { devServer: { proxy: { '/myapi': { // 代理的目标服务器地址 // https://api.douban.com/v2/movie/in_theaters // /myapi/movie/in_theaters target: 'https://douban.uieee.com/v2/', pathRewrite: { '^/myapi': '' }, // 设置https secure: false, // 必须设置该项 changeOrigin: true } } } } // 使用 axios.get('http://localhost:8080/myapi/movie/in_theaters').then(res => { console.log(res) }) axios.get('/myapi/movie/in_theaters').then(res => { console.log(res) })
重新启动 : npm run dev
(配置vue.config.js 都需要重启项目)
keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现。
被缓存的路由,第一次进入页面的时候会调用 created() 方法,第二次进入页面时就不会再调用 created() 方法了,因为页面被缓存起来了。那么我们如果页面改动了,怎么刷新页面呢?虽然 created() 方法不会被调用,但是activated、deactivated这两个生命周期钩子函数会被执行。也就是我们的接口可以写在 activated() 方法中。
// 将缓存 name 为 index 或者 home 的组件,结合动态组件使用
<keep-alive include="index,home">
<router-view :key="key" />
</keep-alive>
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
1. activated
在 keep-alive 组件激活时调用
该钩子函数在服务器端渲染期间不被调用
2. deactivated
在 keep-alive 组件停用时调用
该钩子函数在服务器端渲染期间不被调用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。