JS与Vue笔记
Vue Note
交互方法
v-bind:attr
绑定属性,例如<div v-bind:class="className" ></div>
,data中className就赋值。v-if="xxx"
显示v-for="a in b"
循环当前domv-on:attr="xxx"
用户输入,例如监测点击v-on:click="click"
,需要定义方法:var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { click: function () { method... } } })
v-model
表单与应用状态双向绑定,例如在p中显示input的值:<div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } })
组件
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
var app = new Vue(...)
现在你可以用它构建另一个组件模板:
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol>
用prop定义属性,在组件内使用v-bind
传参:
<div id="app-7">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,稍后再
作详细解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
生命周期钩子
实例中运行的函数,例如created
会在示例创建时调用, this指向vue实例,不要用箭头函数,会指向上一级:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
模板语法
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
这个span的值会替换为rawHtml的值(例如为<span>something</span>
),双大括号不用用在html元素属性上,应该用v-bind
,双大括号中可以用JavaScript表达式
动态参数:2.6.0新增,<a v-bind:[attributeName]="url"> ... </a>
attributeName在data里值为'href' ,则绑定了href属性。
在单页面应用程序中,可以缩写:v-bind:href="xxx"
可缩写为:href='xxx'
,动态参数则缩写为:[key]='xxx'
, v-on:click=xxx
缩写为 @click=xxx
计算属性
逻辑复杂的处理放到计算属性中,为计算属性定义方法,在html中可直接引用,计算属性的this指vue实例本身。
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
访问reverseMessage会调用前面算到的值,除非message值变化才重新运算。
还有watch属性,用于监听数据变化。计算属性和侦听器 — Vue.js (vuejs.org)
条件渲染
有if-else:(2.1.0 新增v-else-if
)
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
用template分组渲染
因为 v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template>
元素当做不可见的包裹元素,并在上面使用 v-if
。最终的渲染结果将不包含 <template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-show
v-show不是增加减少元素,只是改变style的display,不支持 template 与 else。
v-for
与 v-if
一起使用时, v-for
更高级。
事件处理
使用 v-on
监控事件,输入在method属性里的方法:
示例:
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'
表单输入绑定
使用v-model 为表单双向绑定
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
单个复选框,绑定到布尔值;多个复选框,绑定到一数组,数组返回选中复选框的value,单选返回选中的value。
JavaScript
输出显示方法
- 使用
window.alert()
写入警告框 使用
document.write()
写入 HTML 输出,直接输出纯文本<script> document.write(5 + 6); </script>
- 使用
innerHTML
写入 HTML 元素 - 使用
console.log()
写入浏览器控制台
块作用域
let
用 var
关键词声明的变量没有块作用域,在 {}
内使用 let
声明的变量只在 {}
内生效。
若在块外声明,var
与 let
作用相同,都会声明为全局变量(var
声明的属于windows对象, let
声明的不属于)。
同一作用域中 let
不能重新声明变量,var
也不能重新声明又 let
声明的变量。不同作用域可使用let
重复声明同一名字变量(实为两个变量)。
用 var
声明的变量会提升到顶端,即可在声明前调用,而 let
不行。
const
在块作用域内使用 const
声明的变量与 let
变量相似。声明变量后不能修改变量,变量值需要在声明时定义。
const PI;
PI = 3.14159265359; // 错误
const PI = 3.14159265359; // 正确
PI = PI + 10; // 错误,const声明变量不能修改
常量的对象属性可修改:
// 您可以创建 const 对象:
const car = {type:"porsche", model:"911", color:"Black"};
// 您可以更改属性:
car.color = "White";
// 您可以添加属性:
car.owner = "Bill";
// 您可以创建常量数组:
const cars = ["Audi", "BMW", "porsche"];
// 您可以更改元素:
cars[0] = "Honda";
// 您可以添加元素:
cars.push("Volvo");
运算符
===
等值等型!==
不等值或不等型
例如定义 x=5
若有x == '5'
为真(等值),x === '5'
为假(不等型)
三元运算符
variablename = (条件) ? 为真时的值:为假时的值;
var voteable = (age < 18) ? "太年轻":"足够成熟";
函数
定义:function name(par1, par2){}
直接用函数名引用的是函数对象,加括号则是调用函数结果。
function对象
函数实际上是功能完整的对象,可使用function类直接创建函数:
var function_name = new function(arg1, arg2, ..., argN, function_body)
function_body是函数主体(执行的代码)(参数都是字符串),例如:
function sayHi(sName, sMessage) {
alert("Hello " + sName + sMessage);
}
// 可以这样定义
var sayHi = new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");
length
属性声明了函数期望的参数个数:
function doAdd(iNum) {
alert(iNum + 10);
}
function sayHi() {
alert("Hi");
}
alert(doAdd.length); //输出 "1"
alert(sayHi.length); //输出 "0"
arguments对象
在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。
function sayHi() {
if (arguments[0] == "bye") {
return;
}
alert(arguments[0]);
}
高级函数
函数声明:
function foo () {
}
函数表达式:
var foo = function () {
}
函数声明与函数表达式的区别
- 函数声明必须有名字
- 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,例如匿名函数
- 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
函数内 this
指向的不同场景
函数的调用方式决定了 this
指向的不同:
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
若想改变this,可以使用以下函数:
call()
方法调用一个函数, 其具有一个指定的this
值和分别地提供的参数(参数的列表)。
fun.call(指定的this值, 多个原函数的参数...)
apply()
方法调用一个函数, 其具有一个指定的this
值,以及作为一个数组(或类似数组的对象)提供的参数。fun.apply(指定的this值, [原参数1, ... , 原参数N])
两者仅提供参数方法不同。
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
fun.bind(指定的this值, 多个原函数的参数...)
示例:
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
高阶函数
- 函数可以作为参数
- 函数可以作为返回值
- 闭包,函数内可以调用函数外的值,函数内可定义子函数并调用原函数的值。
箭头函数(ES6)
(参数1, 参数2, …, 参数N) => { 函数声明 }
(参数1, 参数2, …, 参数N) => 表达式(单一)
const x = (x, y) => x * y;
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明}
没有参数的函数应该写成一对圆括号:
() => {函数声明}
当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。
箭头函数是不能提升的,所以需要在使用之前定义。
使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。
如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {},这样做是一个比较好的习惯:
箭头函数总是函数表达式;并不存在箭头函数声明。
对 => 的关注多数都在于从代码中去掉 function、return 和 { .. } 节省了那些宝贵的盘输入。但是,目前为止我们省略了一个重要的细节。这一节开头提到 => 函数与 this 绑定行为紧密相关。实际上,=> 箭头函数的主要设计目的就是以特定的方式改变 this 的行为特性,解决 this 相关编码的一个特殊而又常见的痛点。
var controller = {
makeRequest: function(..){
var self = this;
btn.addEventListener( "click", function(){
// ..
self.makeRequest(..);
}, false );
}
};
我们使用了 var self = this 这一 hack,然后引用 self.makeRequest(..),因为在我们传入 addEventListener(..) 的回调函数内部,this 绑定和 makeRequest(..) 本身的 this 绑定是不同的。换句话说,因为 this 绑定是动态的,我们通过变量 self 依赖于词法作用域的可预测性。
这里我们终于可以看到 => 箭头函数的主要设计特性了。在箭头函数内部,this 绑定不是动态的,而是词法的。在前面的代码中,如果使用箭头函数作为回调,this 则如我们所愿是可预测的。
var controller = {
makeRequest: function(..){
btn.addEventListener( "click", () => {
// ..
this.makeRequest(..);
}, false );
}
}
前面代码的箭头函数回调中的词法 this 现在与封装的函数 makeRequest(..) 指向同样的值。换句话说,=> 是 var self = this 的词法替代形式。
对象
构造函数
相当于定义类:用this
, 没有retuen
,创建实例要用到new
function Person(name, age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Person('Jack', 18)
p1.sayName();
在每一个实例对象中同时有一个 constructor
属性,该属性指向创建该实例的构造函数:
p1.constructor === Person // true
prototype
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的所拥有。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。(调高效率)
function Person (name, age) {
this.name = name
this.age = age
}
console.log(Person.prototype)
Person.prototype.type = 'human'
Person.prototype.sayName = function () {
console.log(this.name)
}
var p1 = new Person(...)
var p2 = new Person(...)
console.log(p1.sayName === p2.sayName) // => true
prototype也有属性constructor
只想原函数(Person),复杂的prototype可这样定义,
为了保持 constructor 的指向正确,建议的写法是:
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person, // => 手动将 constructor 指向正确的构造函数
type: 'human',
sayHello: function () {...}
}
字符串模板
模板字面量使用反引号 (`) 而不是引号 ("") 来定义字符串,通过使用模板字面量,您可以在字符串中同时使用单引号和双引号,可以多行,可以用${...}
引用变量与表达式:
let text = `Hello World!`;
let text = `He's often called "Johnny"`;
let text =
`The quick
brown fox
jumps over
the lazy dog`;
let firstName = "Bill";
let lastName = "Gates";
let text = `Welcome ${firstName}, ${lastName}!`;
let total = `Total: ${(price * (1 + VAT)).toFixed(2)}`;
IE不支持模板字面量。
异步
回调函数 callback
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
setTimeout(func, 1000)
就是最经典的回调函数与异步函数例子;或者setInterval(func, 1000)
每间隔Nms执行一次
以下是获取html页面并加载的回调
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function getFile(myCallback) {
let req = new XMLHttpRequest();
req.open('GET', "mycar.html");
req.onload = function() {
if (req.status == 200) {
myCallback(this.responseText);
} else {
myCallback("Error: " + req.status);
}
}
req.send();
}
getFile(myDisplayer);
myDisplayer 是被回调的函数
promise
let myPromise = new Promise(function(myResolve, myReject) {
// "Producing Code"(可能需要一些时间)
myResolve(); // 成功时
myReject(); // 出错时
});
// "Consuming Code" (必须等待一个兑现的承诺)
myPromise.then(
function(value) { /* 成功时的代码 */ },
function(error) { /* 出错时的代码 */ }
);
在Promise接收一个函数作为采纳数,函数的两个参数是resolve与reject,若成功,调用resolve并将操作结果传出,见下:
function greet(){
var promise = new Promise(function(resolve,reject){
var greet = "hello world";
resolve(greet); // 用resolve传出参数
});
return promise;
}
greet().then(v=>{
console.log(v); //输出"hellow world"
})
.then
方法是回调,输出还是promise,所以then后面还可以用then来继续异步操作
async/await
asycn与await是Promise的语法糖,async 使函数返回 Promise,await 使函数等待 Promise
async function myFunc() {
let a = awiat doSomething1();
let b = awiat doSomething2(); // 等doSomething1执行完后2才会执行,即异步
}
其中doSomething1和2需要是Promise?
DOM
查找 HTML 元素
方法 | 描述 |
---|---|
document.getElementById(id) | 通过元素 id 来查找元素 |
document.getElementsByTagName(name) | 通过标签名来查找元素 |
document.getElementsByClassName(name) | 通过类名来查找元素 |
改变 HTML 元素
方法 | 描述 |
---|---|
element.innerHTML = new html content | 改变元素的 inner HTML |
element.attribute = new value | 改变 HTML 元素的属性值 |
element.setAttribute(attribute, value) | 改变 HTML 元素的属性值 |
element.style.property = new style | 改变 HTML 元素的样式 |
添加和删除元素
方法 | 描述 |
---|---|
document.createElement(element) | 创建 HTML 元素 |
document.removeChild(element) | 删除 HTML 元素 |
document.appendChild(element) | 添加 HTML 元素 |
document.replaceChild(element) | 替换 HTML 元素 |
document.write(text) | 写入 HTML 输出流 |
添加事件处理程序
方法 | 描述 |
---|---|
document.getElementById(id).onclick = function(){code} | 向 onclick 事件添加事件处理程序 |
cookie
cookie为保存在本地的名称值对,创建cookie:
document.cookie = "username=Bill Gates";
document.cookie = "username=Bill Gates; expires=Sun, 31 Dec 2017 12:00:00 UTC";
document.cookie = "username=Bill Gates; expires=Sun, 31 Dec 2017 12:00:00 UTC; path=/";
默认情况下,在浏览器关闭时会删除 cookie,通过 path
参数,您可以告诉浏览器 cookie 属于什么路径。默认情况下,cookie 属于当前页。
读取cookie:var x = docment.cookie
会返回所有cookie,可以通过负值直接修改cookie。
删除 cookie 非常简单。删除 cookie 时不必指定 cookie 值:直接把 expires
参数设置为过去的日期即可:
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
cookie是字符串,读取时用decodeURIComponent(document.cookie).split(';');
获取 cookie 的函数
然后,我们创建一个函数返回指定 cookie 的值:
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') { // 若c字符串的第0个字符为' '
c = c.substring(1); // 提取第1个到之后的字符,substring(from, to)为裁剪
} // 还是用while循环去除字符串前所有空格
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
函数解释:
把 cookie 作为参数(cname)。
创建变量(name)与要搜索的文本(CNAME”=”)。
解码 cookie 字符串,处理带有特殊字符的 cookie,例如 “$”。
用分号把 document.cookie 拆分到名为 ca(decodedCookie.split(';'))的数组中。
遍历 ca 数组(i = 0; i < ca.length; i++),然后读出每个值 c = ca[i]。
如果找到 cookie(c.indexOf(name) == 0),则返回该 cookie 的值(c.substring(name.length, c.length)。
如果未找到 cookie,则返回 ""。
总Vue3
组件
组件的props
(不要漏s)里面的属性名,因为html的attribute大小写不敏感,所以用驼峰命名的话,在html里要用横杠隔开,例如:prop:['userName']
在html里要写<xxx user-name='val'></xxx>
组件内元素要监听事件(例如点击),要在对应元素加@click="$emit('eventname', para)"
,para是向上传出的参数,非必要。
在父级(写在html里的)元素添加@eventname="someFunction($event)"
来接收事件并处理,$event
即传出的参数 para
。
v-for与v-if共用
v-if比v-for优先级更高,所以在同一元素内,v-if不能访问v-for内变量,可以将v-for放到外部<template>
标签里面。
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
创建应用
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:
<div id="counter">
Counter: {{ counter }}
</div>
const Counter = {
data() {
return {
counter: 0
}
}
}
Vue.createApp(Counter).mount('#counter')
data必须是函数形式,不能是对象,data()
属于是函数,与小程序很像。
组件化应用构建
在 Vue 中,组件本质上是一个具有预定义选项的实例。在 Vue 中注册组件很简单:如对 app
对象所做的那样创建一个组件对象,并将其定义在父级组件的 components
选项中:
const TodoItem = {
template: `<li>This is a todo</li>`
}
// 创建 Vue 应用
const app = Vue.createApp({
components: {
TodoItem // 注册一个新组件
},
... // 组件的其它 property
})
// 挂载 Vue 应用
app.mount(...)
为组件传参,用props与v-bind。prop为html元素添加属性,然后用v-bind绑定这个属性,例如下面的props:['todo']
,则<todo-item v-bind:todo="变量名">{{todo}}</todo-item>
这样显示的就是变量名对于变量的值。
const TodoItem = {
props: ['todo'],
template: `<li>{{ todo.text }}</li>`
}
现在,我们可以使用 v-bind
指令将待办项传到循环输出的每个组件中:
<div id="todo-list-app">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,稍后再
作详细解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
const ComponentsApp = {
data() {
return {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever else humans are supposed to eat' }
]
}
}
}
const app = Vue.createApp(ComponentsApp)
app.component('todo-item', {
props: ['todo'],
template: `<li>{{ todo.text }}</li>`
})
app.mount('#components-app')