前端
node.js
简介
node.js是一个基于Chrome v8引擎的javaScript 的后端的运行环境
node.js内置的api不包含一些BOM DOM等浏览器内置的api(用v8引擎只是用来解析js代码的)
node.js基于Express框架可以快速构建web应用
基于Electron框架可以构建跨平台的桌面应用
基于restify框架可以快速构建API接口项目
node.js学习路径:
node.js内置API模块(fs,path,http等)+第三方API模块(express,mysql等)
nodemon
nodemon全局工具 在nodejs里面修改代码后要手动close终端,重启我们的项目。用了nodemon之后(即用nodemon app.js命令替换node app.js命令)可以自动帮我们自动重启项目。
Express 中间件:
中间件的分类
- 应用级别的中间件:通过app.use() app.get() app.post()绑定搭配app实例上的中间件
- 路由级别的中间件:绑定到express().Router实例上的中间件(router会绑定get/post方法的)
- 错误级别中间件:参数是(err,req,res,next),多了一个err,而且错误级别中间件必须放在路由处理函数后面
- express()内置的中间件:例如express.static,express.json,express.urlencoded
- 第三方的中间件,npm导包->require导入中间件->app.use()注册使用中间件
- 自定义中间件
Express模块:
用module.exports或者exports来暴露你想暴露的模块
linux常用快捷命令:
使用↑快速复制上一个命令
使用tab快速补全命令
使用esc清空当前已输入命令
使用cls清除终端
JavaScript:
js学习路径
javascript基础语法+浏览器内置API(BOM、DOM)+第三方库(JQuery、art.template)
js分为ECMAScript和webAPIs,webAPIs分为DOM和BOM
nodemon全局工具 在nodejs里面修改代码后要手动close终端,重启我们的项目。用了nodemon之后(即用nodemon app.js命令替换node app.js命令)可以自动帮我们自动重启项目。
ES6
ES是ECMA国际化标准组织制定的一项脚本语言的标准化规范。因为每年都有很多人给js提建议,所以从2015年开始ECMA会在每年6月发布新版本的js,比如es2015,es2016....我们统称es2015及以后的版本为es6.
ES6的一些新语法
let、const、数组解构、对象解构、箭头函数、剩余参数和扩展运算符
Array.form():用来把伪数组转化为一个数组(扩展运算符结合 Array实例.push() 也可以做到)
Array.find(可以放箭头函数):找出第一个符合条件的数组成员,没找到返回undefined
Array.findIndex(可以放箭头函数):找出第一个符合条件的数组成员的索引,没找到返回-1
Array.includes(值):表示某个数组是否含有给定的值
Array.some()
Array.every()
Array.reduce()
模板字符串:使用反引号定义,可以换行,用${}来解析字符串里面的变量,不用做字符串拼接了
String.startWith():表示某字符串是否以参数字符串开始,返回布尔值
String.endWith():表示某字符串是否以参数字符串结尾,返回布尔值
String.repeat():把参数字符串重复几次,返回一个新的字符串
Set数据结构:集合,和数组的区别是,集合里面没有重复的元素 。具体一些api查文档
Vue:
webpack
vue的基本使用步骤
- 导入vue.js文件
- new Vue()构造函数,得到Vue的实例对象
- 声明el(要控制的view)和data数据节点
- 正确的MVVM对应关系:M:model即data数据节点,V:view,VM:Vue实例对象
Vue常见的指令和用法:
内容渲染指令
-
v-text:缺点是会覆盖元素中的默认内容
-
{{}}插值表达式:插值只能用在元素的内容节点,无法用在属性节点
-
v-html:把带有HTML标签的字符串,渲染为真正的DOM元素
比如
'<h3> 这是一个html标签</h3>'这个字符串如果用v-text或者{{}}是无法把他渲染成一个html标签的,用v-html就可以把他渲染成一个真正的DOM元素
属性绑定指令
- v-bind:给属性动态绑定值 简写为一个冒号“:”
事件绑定指令
- v-on: 给一个事件绑定一个我们自己定义的函数,简写为@
事件绑定指令的函数传参问题
注意: 这里以点击事件click为例,还有keyup等键盘相关的事件也可以了解一下
不传参的时候@click="show" ,可以在定义函数的时候写上参数 show(e){},这里的e默认指的是原生的$event,我们可以用e.target来找到触发事件的DOM元素
如果我们想传参又想用原生的event,$event可以放在任意位置
@click="show($event,args1,....)" @click="show(args1,....,$event)"
如果我们不需要使用原生的$event,那么直接使用就行
@click="show(args1,arg2....)"
事件绑定指令的事件修饰符
在处理事件时调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。
为解决这一问题,Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:
.stop.prevent.self.capture.once.passive
具体语法
<button hre='www.baidu.com' @click.prevent='print'>打印一句话并且不跳转到百度界面</button>
按键修饰符
<input @keyup.esc='deleteText'>键盘的esc键弹起的时候删除框内内容</input>
双向数据绑定指令
- v-model:只能用在表单元素上
修饰符
.number 将绑定的数据转化为数字
..trim 除去空格
.lazy 减少频繁更新数据对性能的影响
循环渲染指令
-
v-for
v-for="(item,index) in data里面的数组":key不能是表单的索引,可以用item.id来充当
注意:v-bind:key的取值只能是数字或者字符串,重复渲染相同的ui组件的时候用v-for
条件渲染指令
- v-if:动态创建和移除元素,常和v-else-if、v-else配套使用
- v-show:动态添加和移除display:none样式
二者有区别v-if性能差一些但是现在电脑都很好可以忽略
过滤器
- 过滤器filter(Vue3已经不支持过滤器了)
-
私有过滤器的声明
直接在method里面声明函数
-
私有过滤器的调用(只能给自己Vue实例用)基本语法为
(message|过滤函数)过滤器函数要定义在Vue对象的filters节点下
-
全局过滤器的声明(可以共享给所有的Vue控制的对象)基本语法为
Vue.filter(‘过滤器名字’,function(str){}) -
全局过滤器的调用(和私有过滤器一样)基本语法为
(message|过滤函数)
过滤器是可以连续调用的,例如message|过滤函数1|过滤函数2.....
过滤器可以传参,例如:
message|过滤函数(arg1,arg2)
但写过滤函数的时候默认第一个参数永远都是message
侦听器
- 侦听器
- 侦听器分两类,一类是函数方法,一类是对象方法
//函数方法创建侦听器
username(newVal,OldVal){}
//对象方法创建侦听器
username:{
//侦听器的处理函数
handler(newVal,OldVal){}
}
-
侦听器watch要定义在Vue对象里面的watch节点下,并且要监听哪个数据的变化就把哪个数据的名字作为函数名
-
使用对象方法创建可以通过更改immediate为true使侦听器可以自动触发一次
-
侦听器可以拿到数据变化的新旧值,写在形参里就行了。
-
深度侦听 :只要对象的一个属性变了,就可以触发对象的侦听器,但是稍微影响性能,侦听器返回的也是整个对象
-
如果侦听的是对象的子属性的变化,必须包裹一层单引号,比如
'info.username'(newVal){console.log(newVal)}//这种方法侦听到的返回值只是info的username属性
计算属性
- 计算属性
-
计算属性声明在Vue对象的computed节点下
-
计算属性在声明的时候是用函数方法表示的,方法名就是要计算的属性的名字,函数的返回值就是计算的结果,使用的时候当成普通的属性来使用就可以了
computed:{ rgb(){ return rgb('${this.r},${this.g},${this.b}') } }//任何一个计算属性中依赖的数据项发生了变化,计算属性就会重新赋值
axios
axios是一个专注于网络请求的库
调用axios方法返回的对象是一个Promise对象,因此可以直接链式编程,在axios方法后面加.then(jquery是.success)
const result=axios({
method: 'GET'
url:'http...' })
//result是一个Promise对象
axios的基本使用
- 发起GET请求,还有语法糖版本,比如axios.get(‘url’,{params:{}或者data:{}}),这里懒得写了
axios({
//请求方法
method: 'GET'、
//请求地址
url: '请求地址'
//URL中的查询参数
params: {
id:1
}
//请求体中的参数(这个和params只能有一个)
data: {
}
}).then(function(result){
console.log(result)
})
- 发起post请求,还有语法糖版本,比如axios.post(‘url’,{//直接放data请求体数据}),这里懒得写了
axios({
//请求方法
method: 'GET'、
//请求地址
url: '请求地址'
//URL中的查询参数
params: {
id:1
}
//请求体中的参数(这个和params只能有一个)
data: {
}
}).then(function(result){
console.log(result)
})
//和GET一样,但是data和params只能有一个,一般来说post是data
vue-cli
vue-cli基本介绍
vue-cli是Vue.js开发的标准工具,它简化了程序员基于webpack创建工程化Vue项目的过程
程序员可以专注于在撰写应用上面,而不必花好几天去纠结webpack的配置问题
全局安装
npm install @vue/cli -g
在终端运行如下命令,创建指定名称的项目
vue create 项目名称
Vue项目中src文件构成
- assets 文件夹:存放目录中用到的静态资源文件,例如:CSS样式表、图片资源
- coponents 文件夹:程序员封装的、可复用的组件
- main.js:项目的入口文件(webpack.config.js里面的的entry,默认是index1.js,学webpack的时候学过),要先执行main.js
- App.vue是项目的根组件
vue-cli项目运行的流程
在工程化项目中,vue要做的事情很单纯:通过main.js把App.vue渲染到index.html的指定区域中
其中:
- App.vue用来编写待渲染的模板结构
- index.html中需要预留一个el区域。即index.html中的div app只是一个占位符
- main.js把App.vue渲染到了index.html所预留的区域中,即替换div app
渲染的时候:
new Vue({
render: h => h(App),
}).$mount('#app')
和
new Vue({
el: '#app',
render: h => h(App),
})
效果一样
vue组件
vue组件的构成
- template中定义模板
- script中定义数据
- style中定义样式
vue中data必须是函数是为了保证组件的独立性和可复用性
<template>
<div class="test-box">
<h3>这是用户自定义的Test.Vue----{{username}}</h3>
</div>
</template>
<script>
//默认导出,固定的语法
export default{
//注意:.vue组件中的data不可以像vue-cli之前一样,不能指向对象,这里用函数返回的方法返回username的值
data(){
return {
username:'zhangsan'
}
}
}
</script>
<style>
.test-box{
background-color: pink;
}
</style>
根组件
render函数中,渲染的是哪个.vue组件,那个组件就叫做“根组件”
组件的父子关系
组件的使用(三个步骤:导入、注册、使用)
-
在根组件的scirpt标签中中导入.vue组件
import Left from '@/components/Left/vue' -
在scprit标签中注册组件
export default{ components: { 'Left':Left//当键值对名称一样时,可以直接Left简写 } } -
以标签的形式在div中使用注册好的组件
组件分类
- 私有组件(局部组件)
- 全局组件(全局组件的声明是在main.js里面声明的)
组件的props
组件可以设置自己的props,使得其他组件在使用这个组件的时候可以自定义这个组件的一些值,比如Mycount组件中的count变量在组件A中初始值为10,在组件B中初始值为5,我们无法在Mycount组件中直接return一个count:10或者5,因此我们通过组件的props来解决这个问题
<script>
export default{
props:['init'],//props和data过滤器等是平级的
data(){
return{
count: 0
}
}
}
</script>
使用时
<Mycount init="6"></Mycount>//init就是Mycount的一个属性,他是我们开发者自定义的
注意:上面的6传过去的是字符串 ,下面的init属性前面加了v-bind:表示init绑定的是一个js语句,在js语法中”6“表示数字6
<Mycount :init="6"></Mycount>//6是数字
组件的props是只读的
组件的props是只读的,我们无法通过事件函数直接修改预设好的props的值,否则会报错,解决办法是我们可以用自定义的data里面的数据转存props的值
<script>
export default{
props:['init'],
data(){
return{
count: this.init
}
}
}
</script>
组件default配置
为了解决用户可能没有给组件标签传初始值,我们需要给组件设置一个初始值,但这种复杂操作的时候我们不能简单的用数组来定义props,我们需要用对象的形势来写
<script>
export default{
props: {
init: {
//如果外界使用Mycount组件的时候没有传递init属性,则init里面default配置好的默认值生效
default: 10
}
}
}
</script>
组件的type配置
为了解决用户使用组件的时候传过来的数据不合法导致一些bug,我们可以提前设置好我们组件的属性需要的值类型
<script>
export default{
props: {
init: {
//如果外界使用Mycount组件的时候没有传递init属性,则init里面default配置好的默认值生效
default: 10
//init的值类型必须是Number数字类型
type: Number
}
}
}
</script>
组件的require配置
require配置表示这个组件的某个属性为必填项
<script>
export default{
props: {
init: {
//如果外界使用Mycount组件的时候没有传递init属性,则init里面default配置好的默认值生效
default: 10
//init的值类型必须是Number数字类型
type: Number
//表示如果不给init传值的话会报错
require: true
}
}
}
</script>
组件的样式冲突
scoped解决样式全局生效
每个组件的style标签里面的css样式都是全局生效的,比如我给一个组件里面的h3绑定了颜色,他也会影响到其他组件里面的h3的样式。
//老写法
h3{
color: red
}
为了解决这个问题我们可以给每个标签加上自己的属性,在定义样式的时候不仅仅用标签选择器,可以采用标签选择器+属性选择器来区分开在不同组件中的标签。
<h3 data-v-001>这是一个三级标题<h3>
vue组件中的css新写法
//新写法
h3[data-v-001]{
color: red
}
但是如果给每个标签都加上这样的自定义属性来区别标签太麻烦了。我们可以在.vue组件中的style标签里加上scoped属性,vue会自动帮我们给每个标签加上类似data-v-001这样的属性
//最终写法
<style lang="less" scoped>
h3{
color: red
}
</style>
/deep/解决父组件中修改子组件样式
如果我们想在父组件中修改子组件样式,如果只用标签选择器的话会修改全局的同种标签样式,如果给父组件中的style标签加scoped属性的话我们又无法单单通过标签选择器选中子组件,因为scoped会给父组件的所有标签自动打上一个属性以作区分。
解决办法是使用后代选择器
//语法是[]+空格+标签,表示data-v-001这个id下的h3标签
[data-v-001] h3{
color: red
}//data-v-001表示父组件的标签,h3表示我们想找到子组件里面的h3标签并且修改他的样式,这种后代选择器正好满足我们的需求
但我们通常不知道这个data-v-001是多少,所以我们用 /deep/ 来代替
//语法是/deep/+空格+标签
/deep/ h3{
color: red
}
.vue文件和vue实例的本质
.vue文件本质
.vue文件只是一个模板。浏览器只能直接解析html的文件,但是无法直接解析.vue这个文件。
.vue这个文件会经过pakage.json里面的一个包"vue-template-compiler": "^2.6.14"的解析生成js代码。并通过script标签包装后丢到html里面来进行显示
组件的实例
我们自己定义的一些.vue组件只是一个模板结构的声明或者说是一个构造函数的声明,只有我们在其他vue组件里面使用的它的时候,其他的vue文件经过vue-template-compiler解析生成js代码后会在对应的位置new一个组件实例,因此我们些的vue组件只是一个模板一个构造函数,当我们用标签的形式真正使用它的时候才是创建一个vue实例。
组件的生命周期
组件的生命周期强调时间段
组件的生命周期函数强调时间点,具体为我们在组件的生命周期特殊的时间点上可以做那些事情,把这些想做的事情放在组件的生命周期函数中,程序会自动执行。
组建的生命周期函数分类
- 组件创建阶段
- new Vue()
- beforeCreate
- created
- beforeMount
- mounted
- 组件运行阶段
- beforeUpdate
- updated
- 组件销毁阶段
- beforeDestroy
- destroyed
1.2.3.4.5.6.7.8.9.连起来即是一个组件的生命周期了
组件之间的数据共享
父组件向子组件共享数据
自定义属性
父组件里只管定义数据和传数据(记得用v-bind,否则传过去的是字符串,而不是属性对应的值)
子组件只负责接受数据
//父组件
<Son :msg="message" :uer="userInfo"></Son>
data(){
return {
message: "hello vue.js",//这里message为简单类型,传过去的时候传的是复制的值
userInfo: {name: 'zs',age: 20}//这里userInfo是对象为复杂类型传过去传的是引用
}
}
//子组件
<template>
<div>
<p>这是Son组件</p>
<p>父组件传递过来的msg值为{{msg}}</p>//这里message为简单类型,传过去的时候传的是复制的值
<p>父组件传递过来的user值为{{user}}</p>//这里userInfo是对象为复杂类型传过去传的是引用
</div>
</template>
props: ['msg','user']
子组件向父组件共享数据
利用自定义事件
//子组件
export default {
data(){
return {
count: 0
}
},
methods: {
add() {
this.count+=1
//修改数据的时候通过$emit()函数来触发自定义事件numchange(我们也可以通过这个联想click事件是如何触发的)
this.$emit('numchange',this.count)//this.count就是自定义事件numchange要传给父组件的值
}
}
}
//父组件
<Son @numchage="getNewCount"></Son>
export default {
data() {
return {
countFromSon: 0
}
},
methods: {
getNeCount(val) {//这里的val是从numchange这个自定义事件里面传过来的
this.countFromSon=val
}
}
}
兄弟组件之间共享数据
在vue2.x中,兄弟组件之间数据共享的方案是EventBus
兄弟组件A(数据发送方)
<button @click="send">sendMymessage</button>//这里发送数据
<hr />
</div>
</template>
<script>
//导入eventBus模块
import bus from './eventBus.js'
export default {
props: ['msg', 'user'],
data() {
return {
str: "这是兄弟之间的消息传递",
arr: [
{ id: 1, name: 'zs' },
{ id: 2, name: 'ls' }
]
}
},
method: {
send(){
//利用eventBus的实例来传递值
bus.$emit('sendMymessage',this.str)
}
}
}
</script>
兄弟组件B(数据接收方)
<p>{{messageFormLeft}}</p>//在这里显示一下A组件传过来的str的效果
</div>
</template>
<script>
//导入eventBus模块
import bus from './eventBus.js'
export default {
//定义要接受的数据的载体
data(){
return {
messageFormLeft: ''
}
},
created(){//记得在created中调用bus.$on方法
bus.$on('send',val=>{
//箭头函数接受到send传过来的val,想办法把它转存给本组件的data里,即转存到messageFromLeft
this.messageFormLeft=val
})
}
}
</script>
eventBus.js的代码,真的只有这么点。。。
import Vue from "vue"
export default new Vue()
ref
通过ref操控原生的DOM元素
Vue的特点是数据绑定,要我们程序员亲自操作DOM的情况很少,但如果你想操控DOM的话,Vue实例的开头的属性都是Vue自带的一些属性)
给标签定义ref属性的时候ref不可以冲突(推荐起名规则为:’标签名简写+Ref‘)若冲突以最下面那个算
<template>
<div >
<h1 ref="myh1">APP根组件</h1>//关键代码在这
<button @click="changeColor">改变标题的颜色为红色</button>
</div>
</template>
<script>
export default {
methods: {
changeColor(){
this.$refs.myh1.style.color='red'//关键代码在这
}
}
}
</script>
<style lang="less">
</style>
通过ref操控自己定义的Vue组件
我们可以通过$ref轻松调用子组件的方法、修改子组件的属性
<template>
<div >
<h1 ref="myh1">APP根组件</h1>
<button @click="changeColor">改变标题的颜色为红色</button>
<button @click="resetCount">重置Left组件中count的值</button>
<Left ref="myLeft"></Left>//操控自己定义的组件Left
</template>
<script>
import Left from './components/Left.vue'
export default {
components: { Left },
methods: {
changeColor(){
this.$refs.myh1.style.color='red'
},
resetCount(){
this.$refs.myLeft.count=0
}
}
}
</script>
<style lang="less">
</style>
<template>
<div >
<h3>Left组件--count的值为{{count}}</h3>
<button @click="count+=1">count+1</button>
</div>
</template>
<script>
export default {
data(){
return {
count: 0
}
}
}
</script>
<style lang="less" scoped>
</style>
由ref引申出的问题
如何保证cb回调函数可以操作到最新的DOM元素?(cb=call back)
如果我修改一个组件的属性值之后立即执行一个函数,那么此时数据更新后但页面还没来的及渲染,如果这个函数里面的一些语句用到了渲染后的页面的一些属性,那么此时代码就会报错。我们此时需要利用this.$nextTick(cb回调函数)来使我们需要执行的函数稍微延期执行,并且把函数放在updated节点里也是不对的,如果是input变button的时候还是会报错
记得在回调函数里面放我们需要执行的函数,而不是直接放在nextTick的参数里面。
错误做法:
<template>
<div >
<h1 ref="myh1">APP根组件</h1>
<input type="text" v-if="inputVisible" @blur="showButton" ref="inpRef"/>
<button v-else @click="showInput">展示输入框</button>
<Left ref="myLeft"></Left>
</div>
</template>
<script>
import Left from './components/Left.vue'
export default {
data(){
return {
inputVisible: false
}
},
components: { Left },
methods: {
showInput(){
this.inputVisible=true
this.$refs.inpRef.focus()//这样写会报错,因为页面还没渲染出来input输入框,因为v-if隐藏了它
},
showButton(){
this.inputVisible=false
}
}
}
</script>
正确做法:
methods: {
showInput(){
this.inputVisible=true
//正确做法
this.$nextTick(()=>{
this.$refs.inpRef.focus()
})
},
showButton(){
this.inputVisible=false
}
}