保姆级前端学习笔记(包含vue2、vue-cli)

52 阅读12分钟

前端

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 中间件:

中间件的分类

  1. 应用级别的中间件:通过app.use() app.get() app.post()绑定搭配app实例上的中间件
  2. 路由级别的中间件:绑定到express().Router实例上的中间件(router会绑定get/post方法的)
  3. 错误级别中间件:参数是(err,req,res,next),多了一个err,而且错误级别中间件必须放在路由处理函数后面
  4. express()内置的中间件:例如express.static,express.json,express.urlencoded
  5. 第三方的中间件,npm导包->require导入中间件->app.use()注册使用中间件
  6. 自定义中间件

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常见的指令和用法:

内容渲染指令

  1. v-text:缺点是会覆盖元素中的默认内容

  2. {{}}插值表达式:插值只能用在元素的内容节点,无法用在属性节点

  3. v-html:把带有HTML标签的字符串,渲染为真正的DOM元素

    比如'<h3> 这是一个html标签</h3>'这个字符串如果用v-text或者{{}}是无法把他渲染成一个html标签的,用v-html就可以把他渲染成一个真正的DOM元素

属性绑定指令

  1. v-bind:给属性动态绑定值 简写为一个冒号“:”

事件绑定指令

  1. v-on: 给一个事件绑定一个我们自己定义的函数,简写为@
事件绑定指令的函数传参问题

注意: 这里以点击事件click为例,还有keyup等键盘相关的事件也可以了解一下

不传参的时候@click="show" ,可以在定义函数的时候写上参数 show(e){},这里的e默认指的是原生的$event,我们可以用e.target来找到触发事件的DOM元素

如果我们想传参又想用原生的event,那么我们需要在函数的参数中声明event,那么我们需要在函数的参数中声明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>

双向数据绑定指令

  1. v-model:只能用在表单元素上
修饰符

.number 将绑定的数据转化为数字

..trim 除去空格

.lazy 减少频繁更新数据对性能的影响

循环渲染指令

  1. v-for

    v-for="(item,index) in data里面的数组"
    

    :key不能是表单的索引,可以用item.id来充当

注意:v-bind:key的取值只能是数字或者字符串,重复渲染相同的ui组件的时候用v-for

条件渲染指令

  1. v-if:动态创建和移除元素,常和v-else-if、v-else配套使用
  2. v-show:动态添加和移除display:none样式

二者有区别v-if性能差一些但是现在电脑都很好可以忽略

过滤器

  1. 过滤器filter(Vue3已经不支持过滤器了)
  • 私有过滤器的声明

    直接在method里面声明函数

  • 私有过滤器的调用(只能给自己Vue实例用)基本语法为

    (message|过滤函数)
    

    过滤器函数要定义在Vue对象的filters节点下

  • 全局过滤器的声明(可以共享给所有的Vue控制的对象)基本语法为

    Vue.filter(‘过滤器名字’,function(str){})
    
  • 全局过滤器的调用(和私有过滤器一样)基本语法为

    (message|过滤函数)
    

过滤器是可以连续调用的,例如message|过滤函数1|过滤函数2.....

过滤器可以传参,例如:

message|过滤函数(arg1,arg2)

但写过滤函数的时候默认第一个参数永远都是message

侦听器

  1. 侦听器
  • 侦听器分两类,一类是函数方法,一类是对象方法
//函数方法创建侦听器
username(newVal,OldVal){}
//对象方法创建侦听器
username:{
    //侦听器的处理函数
    handler(newVal,OldVal){}
}
  • 侦听器watch要定义在Vue对象里面的watch节点下,并且要监听哪个数据的变化就把哪个数据的名字作为函数名

  • 使用对象方法创建可以通过更改immediate为true使侦听器可以自动触发一次

  • 侦听器可以拿到数据变化的新旧值,写在形参里就行了。

  • 深度侦听 :只要对象的一个属性变了,就可以触发对象的侦听器,但是稍微影响性能,侦听器返回的也是整个对象

  • 如果侦听的是对象的子属性的变化,必须包裹一层单引号,比如

    'info.username'(newVal){console.log(newVal)}//这种方法侦听到的返回值只是info的username属性

计算属性

  1. 计算属性
  • 计算属性声明在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的基本使用

  1. 发起GET请求,还有语法糖版本,比如axios.get(‘url’,{params:{}或者data:{}}),这里懒得写了
axios({
    //请求方法
    method: 'GET'、
    //请求地址
    url: '请求地址'
    //URL中的查询参数
    params: {
    id:1
    }
    //请求体中的参数(这个和params只能有一个)
      data: {
    }
}).then(function(result){
        console.log(result)
})
  1. 发起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组件,那个组件就叫做“根组件”

组件的父子关系

组件的使用(三个步骤:导入、注册、使用)

  1. 在根组件的scirpt标签中中导入.vue组件

    import Left from '@/components/Left/vue'
    
  2. 在scprit标签中注册组件

    export default{
        components: {
            'Left':Left//当键值对名称一样时,可以直接Left简写
        }
    }
    
  3. 以标签的形式在div中使用注册好的组件

组件分类

  1. 私有组件(局部组件)
  2. 全局组件(全局组件的声明是在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实例

组件的生命周期

组件的生命周期强调时间段

组件的生命周期函数强调时间点,具体为我们在组件的生命周期特殊的时间点上可以做那些事情,把这些想做的事情放在组件的生命周期函数中,程序会自动执行。

组建的生命周期函数分类

  • 组件创建阶段
  1. new Vue()
  2. beforeCreate
  3. created
  4. beforeMount
  5. mounted
  • 组件运行阶段
  1. beforeUpdate
  2. updated
  • 组件销毁阶段
  1. beforeDestroy
  2. 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实例的refs属性可以帮助你。(vue实例对象中凡是refs属性可以帮助你。(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
    }
  }