VUE
由于笔记是从电脑上复制过来的,图片无法加载过来,故缺失了很多,如果有人看,看目录的学习路径,仅供参考
编程范式:
原生的js,jquery是命令式编程
现在的vue是声明式编程:可以做到页面和数据完全分离
在这里我想说一下,无论是前端还是后端," 约定大于配置 "是很重要的
对象里的方法称为方法,单独的称为函数
生命周期:执行一系列显示和隐式函数的过程叫做生命周期
1.1.认识语法
1.2.事件监听(v-on)
在vue中,使用v-on来监听事件,其缩写为:@
<div id="app">
<h1>{{count}}</h1>
<button v-on:click="add">+</button>
<button @click="sub">-</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add: function (){
console.log("nice"),
this.count++
},
sub: function (){
console.log("nice1"),
this.count--
}
}
});
</script>
v-on的传参问题:
在事件定义时,写函数省略了小括号,但是方法本身是需要一个参数的
如果函数需要参数,但是没有传入,有小括号时,那么函数的形参为undefined
如果既没有参数,更没有小括号,那这就过分了,不过就算这么过分,也是能运行的,它会传递的时此时触发的Event
v-on的修饰符
为了防止事件冒泡,vue提供了修饰符
stop:这个事件触发后立即停止,不触发其他事件
prevent:阻止默认事件,自定义提交事件
input:监听用户输入的事件
keyup / keydown:监听键盘的点击事件
once:只触发一次的回调
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<div @click="divClick">div按钮</div>
<button @click.stop="btnClick">按钮</button>
<form action="submit" >
<input type="submit" value="提交" @click.prevent="submitClick">
<input type="text" value="键盘事件" @keyup="submitClick">
<input type="text" value="键盘事件" @keyup.enter="submitClick">
<input type="text" value="键盘事件" @click.once="submitClick">
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
message: 'hello'
},
methods: {
btnClick(){
console.log("btnclick");
},
divClick(){
console.log("divClick");
},
submitClick(){
console.log("subClick");
}
}
});
</script>
</body>
</html>
1.3.计算属性(computed)
计算属性在去名字时不要带有动词
? 把原有的属性通过某种方式显示出来,这个过程叫做计算属性,且计算属性虽然写法是函数,但放在computed里就成了属性,调用时并不需要加小括号
? 计算属性与methods区别:
计算属性在多次调用中只调用一次,拥有缓存,methods调用几次就执行几次,没有缓存
get和set方法
计算属性一般没有set方法,是只读属性
<div id="app">
<h1>{{fullName}}</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
const app = new Vue({
el: '#app',
data: {
movies: ['nice','one','power','electirc'],
isActvie: true,
btnindex: 0,
onesie: 13
},
computed: {
fullName: {
get: function (){
return this.btnindex+' '+this.onesie
}
}
}
});
</script>
------只读的话,可以这样写,get可以省略
computed: {
fullName: function (){
return this.btnindex+' '+this.onesie
}
}
1.4.条件判断
v-if ------------- v-else-if --------------v-else
不必多写了吧,看到这个没反应过来,就重学吧
为了避免服用,可以加入表示,key
key:作为标识,标识一样时可复用,标识不一样,表示不可复用
<div id="app">
<span v-if="isActvie">
<label for="username">用户账号</label>
<input id="username" type="text" placeholder="用户账号" key="username">
</span>
<span v-else>
<label for="useremail">用户邮箱</label>
<input id="useremail" type="text" placeholder="用户邮箱" key="usercode">
</span>
<button type="button" @click="isActvie=!isActvie">切换类型</button>
</div>
v-show
v-if:当条件为false时,包含v-if指令的元素,不存在于dom中,被删掉,切换一次使用这个
v-show:当条件为false时,v-show只是给我们的元素添加一个行内样式:display:none,切换频繁时使用这个
ES6
2.1.块级作用域
let比var更优秀的地方,就在于块级作用域
var作用域没有限制,会造成程序的执行是无序的
let是ES6开始才诞生的,在ES6之前,只存在var
2.2.const
建议:在ES6开发中,优先使用const,只有需要改变某一个标识符的时候才使用let
const修饰的标识符不能进行修改
用const定义标识符时,必须进行赋值
常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
const ob={
name: wang,
age: 18
},
ob.name=zhang,
ob.age=20
2.3.ES6增强写法
//1. 属性的增强写法
const name = 'why';
const age = 18;
const obj = (
name,
age
)
//2.函数的增强写法
const obj1={
run(){
},
eat(){
}
}
循环遍历
在遍历的时候,小括号内,遍历的属性越重要就越靠前
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性
v-for遍历数组
v-for="item in items " //无使用下标值
v-for = “(item,index) in items” //获取下标值
v-for遍历对象
v-for=“item in info”
//在遍历对象的过程中,如果只是获取一个值,那么获取到的是value
v-for = “(item,key,index) in items”
//获取value和key和下标
v-for绑定key(diff算法):
v-for使用时需要绑定key,是为了更高效的更新虚拟DOM
有key是单向排序,即插入,无key是重构双向排序,即排序迭代直到把位置迭对
< v-for=“item in items” :key=“item”>
能做到响应式的一些方法: push() / pop() / shift() / unshift() / splice() / sort()
注意:通过索引值修改属性并不是响应式,vue并没有监听这种方式改变数组
可变参数:可以传n个参数
function(…items){
}
function(…items:T[]){
}
for (let in this.books)
//in型遍历,遍历的是对象,只能遍历下一级对象的个数,不能越级遍历对象的数据
for (let i of this.books) {
totalPrice += i.price * i.count
}
//of形遍历,遍历的是数组,能遍历出数组中的数据,不过比较迟性能
高阶函数的使用
for循环高阶函数的使用(filer的使用)
加入了回调函数
回调函数返回的是boolean类型
如果为true,就会创建空间加入数组里
Map函数的使用
对数组里的每个数进行遍历,并创建空间赋值给数组
Reduce函数的使用
至少需要传两个值,第一个需要是函数,第二个传初始化值,初始化值一般为0
对所有数据进行同一操作
与其说是汇总,不如说是根据已有的参数返回一个表达式吧
回调函数里,第一个参数是初始化的值,第二个参数是数组里的数据
高级用法
filter,筛选条件的函数-》Map,映射数据改变的函数-》reduce,使用函数表达式的函数,我悟了,函数式编程
大师,我又悟了,原来箭头函数省略了写function的步骤
箭头函数是简写的匿名函数
箭头函数this指向是父函数的this指向
表单绑定v-model—双向绑定
radio表单类型的结合
没有v-model的情况下,需要添加name,且name的值是一样的,才会互斥,表单提交是将name作为key提交的
checkbox选框类型的结合 select类型的结合
值绑定
v-model修饰符的使用
lazy:懒加载,作用是延迟加载,例如,input鼠标离开框里面后,不在有选中,失去焦点,才会加载
number:input里限定输入类型为数字类型
trim:如果首尾有很多空格,通常我们希望将其去除,可以使用trim修饰符可以过滤内容左右两边的空格
组件的使用
<div id="app">
<!-- 复用组件-->
<my-cpn></my-cpn>
</div>
//组件内容,记得放在 漂`` 里面
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
//创建组件构造器对象
const cpnC=Vue.extend({
template: `<div>
<h2>hello</h2>
</div>`
});
//注册组件(全局组件,可以在多个vue实例下使用)
Vue.component('my-cpn',cpnC);
var vm = new Vue({
el: "#app",
data:{
message: "hello,vue"
},
components: { //在这里面注册的组件为局部组件,只能在一个实例中使用
cpn: cpnC //cpn是使用组件时的标签名
}
});
</script>
父组件与子组件
子组件只能通过父组件使用,不可以单独使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
//创建第二个组件构造器
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>2333</p>
</div>
`
});
const cpnC2 = Vue.extend({//父组件
template: `
<div>
<h2>我是标题</h2>
<p>qwq</p>
<cpn1></cpn1> //子组件
</div>
`,
components: { //在这里面注册的组件为局部组件,只能在一个实例中使用
cpn1: cpnC1, //cpn1是使用组件时的标签名
}
});
var vm = new Vue({
el: "#app",
data: {
message: 'how are you',
},
components: { //在这里面注册的组件为局部组件,只能在一个实例中使用
cpn2: cpnC2 //cpn2是使用组件时的标签名
}
});
</script>
</body>
</html>
组件的语法糖注册方式与分离写法
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><div id="app"> <cpn></cpn> <cpn2></cpn2></div><script type="text/x-handlebars-template" id="cpn"> <div> <h2>我是标题</h2> <p>维生素哈哈哈</p> </div></script>//可以换成template<script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script><!-- 语法糖的分离注册写法--> Vue.component('cpn',{ template: '#cpn' });<!-- 语法糖全局注册,内部帮助做了extend--> // Vue.component('cpn1',{ // template: ` // <div> // <h2>我是标题</h2> // <p>2333</p> // </div> // ` // }) //创建第二个组件构造器 // const cpnC1 = Vue.extend(); var vm = new Vue({ el: "#app", data: { message: 'how are you', }, components: { //语法糖局部注册 'cpn2': { template: ` <div> <h2>我是标题</h2> <p>2333</p> </div> ` } } });</script></body></html>
组件的data必须是函数,不能访问vue实例
组件是一个单独功能模块的封装
- 这个模块有属于自己的html模块,也应该有属于自己的数据data,且也是避免vue实例过于臃肿
组件自己的数据应有一个data属性,这个data属性为函数
这个函数返回一个对象,对象内部保存着数据
<div> <h2>{{abc}}</h2> <p>维生素哈哈哈</p> </div></template><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script><!-- 语法糖的分离注册写法--> Vue.component('cpn',{ template: '#cpn', data() { return { title: 'abc', } } });
使用data函数的原因是,避免多个模块指向同一个对象,指向同一个内存地址,因此使用data函数,在每次使用时创建一个新的对象
<div id="app"> <cpn></cpn></div><template id="cpn"> <div> <h2>当前计数: {{counter}}</h2> <button @click="increment">+</button> <button @click="decrement">-</button> </div></template><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script><!-- 语法糖的分离注册写法--> Vue.component('cpn',{ template: '#cpn', data() { return { counter: 0, } }, methods: { increment(){ this.counter++; }, decrement(){ this.counter--; } } }); var vm = new Vue({ el: "#app", data: { message: 'how are you', } });</script>
父子组件的通信
Vue官方提到: 父组件通过props向子组件传递数据
? 子组件通过事件向父组件发送消息
两者本质其实一样
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><div id="app"> <cpn :cmovies="movies" :cmesssage="message"></cpn></div><template id="cpn"> <div> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> <p>{{cmovies}}</p> <h2>{{cmesssage}}</h2> </div></template><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script> const cpn ={ template: '#cpn', // props: ['cmovies','cmesssage'], props: { //类型限制 // cmovies: Array, //要求传过来的是数组 // cmesssage: String //要求传过来的是string //提供一些默认值 cmesssage: { type: String, default: 'aaaa', required: true //这个作用时,使用这个组件,这个属性比传,不穿就报错 }, cmovies: { //类型是对象或者数组时,默认值必须是一个函数 type: Array, default(){ return [] } } }, data(){ return{} }, } var vm = new Vue({ el: "#app", data: { message: 'how are you', movies: ['海王','哪吒重生'] }, components: { cpn } });</script></body></html>
父传子过程中props的驼峰命名
在传递过程中props的驼峰标识,使用起来很蛋疼,绑定的时候需要用横杠隔开大小写,再把大写写为小写
因此,极度不建议在传递过程中用驼峰命名props
子传父自定义事件($emit)
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><!--父组件模板--><div id="app"><!-- 这个参数可加可不加,因为它的传递是通过其它方式--><cpn @itemclick="cpnClick(item)"></cpn></div><!--子组件模板--><template id="cpn"> <div> <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button> </div></template><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script> const cpn ={ template: '#cpn', data(){ return{ categories: [ {id: 'aaa', name: '热门推荐'}, {id: 'bb', name: '手机数码'}, {id: 'ccc', name: '家用家电'}, {id: 'ddd', name: '电脑办公'}, ] } }, methods: { btnClick(item){ this.$emit('itemclick',item)//发射itemclick事件,避免使用驼峰 } } } var vm = new Vue({ el: "#app", data: { message: 'how are you', movies: ['海王','哪吒重生'] }, components: { cpn }, methods: { cpnClick(item){ console.log('cpnClick'); } } });</script></body></html>
组件化开发
如果需要改变组件里的props值,需要通过计算属性或数据集创建一个同样的属性来绑定,用于同步更改
父子组件的访问方式
------父访问子: $children 或 $refs (来源于reference引用)
一般使用refs,因为refs指定的属性,而children是用下标来索引的,下标容易改变,因此一般使用refs
children一般用于访问全部子组件时使用的
------子访问父:$parent
插槽(slot)
组件的插槽是为了让我们封装的组件更加具有扩展性
结构一样,内容不一样一般就使用插槽
-
插槽的基本使用 -
插槽的默认值使用 -
如果有多个值,同时放入到组件进行替换时,一起作为替换元素 具名插槽 指定插槽需要插入的类型 <div id="app"> <cpn><span>标题</span></cpn> <cpn><span slot="left">标题</span></cpn></div><template id="cpn"> <div> <slot name="left">left</slot> <slot name="right">right</slot> </div></template>
作用域插槽的使用
官方准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
总结:父组件替换插槽的标签,但是内容由子组件来提供
slot-scope是引用插槽的关键字
<div id="app"> <cpn> <!-- 取到template的插槽对象--><!-- 这个template可以改其它名字,slot-scope存在就行,使用template只是为了低版本的vue支持这种写法--> <template slot-scope="slot"> <span v-for="item in slot.aabb">{{item}} -- </span> </template> </cpn> <cpn> <template slot-scope="slot"><!-- 使用join函数分割字符串--> <span>{{slot.aabb.join(' - ')}}</span> <!-- 取到template的data--> </template> </cpn></div><template id="cpn"> <div> <slot :aabb="pLanguages"> <ul> <li v-for="item in pLanguages">{{item}}</li> </ul> </slot> </div></template><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script> const vm = new Vue({ el: "#app", data:{ message: ["YND","adsf",'adc'] }, components: { cpn : { template: '#cpn ', data(){ return{ pLanguages: ['Js','C#'] } }, // create(){ // this.pLanguages.join(' --- ') // // 把数组转换成字符串,多个字符串之间以一个符号连接 // } } } });
模块化开发,第二阶段
ajax这种东西,学前端学到通信就可以了,之后转战vue的,对于后端来说,学习vue就行了,ajax对ta侧重后端来说,狗都不学
原生js真是太傻了,各自干涉,耦合性太高,如果使用匿名闭包,又无法复用,模块化才是趋势,而基于模块化诞生的vue做到的不只是前后端分离,还做到了页面工程分离
CommonJs了解
这个用的不多,基本用ES6
使用export 和 require 是为了解析语法
ES6模块化
使用的时候加入module,就会以模块的形式编译
模块与模块之间不能直接使用其它模块的东西
这个东西要注意以下,导入的后缀不要少,因为idea默认跨域请求,
这个写法是简单写法,有漏洞
import * as a form './*'
a.flag
WebPack
从本质上来说,Webpack是一个现代的JS应用的静态模块打包工具
总结以下,两个关键词:模块 和 打包
通过webpack做底层支持了多种模块化方案
webpack:万物皆可模块
gulp(做了解):
gulp的核心是Task,可以配置一系列task,并且定义task要处理的事务(例如ES6,ts转换,图片压缩,scss转成css)
之后让gulp来依次执行这些task,而且让整个流程自动化
所以gulp也被称为前端自动化任务管理工具
webpack安装
-
安装webpack需要先安装node.js -
全局安装webpack,先指定3.6.0版本,因为vue cli2依赖于这个版本以上
npm install webpack@5.48.0 -g
这里我用的版本比较新,用这个没问题
-
局部安装webpack
npm install webpack@5.48.0 --save-dev
–save-dev 是开发时依赖,项目打包后不需要继续使用
-
为什么全局安装后,还需要局部安装?
- 在终端执行webpack命令,使用的全局安装webpack
- 当在package.json中定义了sripts时,其中包含了webpack命令,那么使用的是局部webpack
dist 和 src目录,dist放打包的东西,src放工程代码
打包指令:
webpack src/main.js dist/bundle.js
const path = require('path')
moudule.exports = {
entries: './src/main.js',
output: {
path: path.resolve(__dirname,'dist'),
filename: 'bundle.js'
}
}
loader
loader是webpack中的核心概念
https://webpack.js.org/loaders/css-loader/#root
使用多个loader时,从右向左读
import css from "./src/css/normal.css";
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname,'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
}
|