D3近年來一直是JavaScript最重要的數據可視化庫之一,在創(chuàng)建者Mike Bostock的維護下,前景依然無量,至少現在沒有能打的:
于此,我們不需要從D3 DOM操作功能開始學起,直接通過實例來入門D3。
以下實例的模版均為以下形式:
<html>
<head>
<link rel="stylesheet" href="index.css">
<title>Learn D3.js</title>
</head>
<body>
<!--或其它標簽-->
<h1>First heading</h1>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="index.js"></script>
</body>
</html>
復制代碼
你需要學習的第一件事是如何使用D3.js選擇和操作DOM元素。該庫在操作DOM方面實際上非常強大,因此理論上可以將其用作jQuery的替代品。以下代碼請逐行添加運行。
// index.js
d3.select();
d3.selectAll();
d3.select('h1').style('color', 'red')
.attr('class', 'heading')
.text('Updated h1 tag');
d3.select('body').append('p').text('First Paragraph');
d3.select('body').append('p').text('Second Paragraph');
d3.select('body').append('p').text('Third Paragraph');
d3.selectAll('p').style('')
復制代碼
當你要創(chuàng)建可視化時,了解如何加載數據以及將其綁定到DOM非常重要。所以在這個實例中,你將學到這兩點。
let dataset = [1, 2, 3, 4, 5];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p') // appends paragraph for each data element
.text('D3 is awesome!!');
//.text(function(d) { return d; });
復制代碼
首先需要添加一個svg標簽
<h1>Bar Chart using D3.js</h1>
<svg class="bar-chart"></svg>
復制代碼
然后在index.js中添加(已添加關鍵注釋):
// 數據集
let dataset = [80, 100, 56, 120, 180, 30, 40, 120, 160];
// 定義svg圖形寬高,以及柱狀圖間距
let svgWidth = 500, svgHeight = 300, barPadding = 5;
// 通過圖形計算每個柱狀寬度
let barWidth = (svgWidth / dataset.length);
// 繪制圖形
let svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
// rect,長方形
// 文檔:http://www.w3school.com.cn/svg/svg_rect.asp
let barChart = svg.selectAll("rect")
.data(dataset) //綁定數組
.enter() // 指定選擇集的enter部分
.append("rect") // 添加足夠數量的矩形
.attr("y", d => svgHeight - d ) // d為數據集每一項的值, 取y坐標
.attr("height", d => d) // 設定高度
.attr("width", barWidth - barPadding) // 設定寬度
.attr("transform", (d, i) => {
let translate = [barWidth * i, 0];
return "translate("+ translate +")";
}); // 實際是計算每一項值的x坐標
復制代碼
這時就需要在上述代碼中創(chuàng)建svg的 text文本
let text = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(d => d)
.attr("y", (d, i) => svgHeight - d - 2)
.attr("x", (d, i) => barWidth * i)
.attr("fill", "#A64C38");
復制代碼
過程比較簡單,就是返回文本,計算x/y坐標,并填充顏色。
D3中有個重要的概念就是比例尺。比例尺就是把一組輸入域映射到輸出域的函數。映射就是兩個數據集之間元素相互對應的關系。比如輸入是1,輸出是100,輸入是5,輸出是10000,那么這其中的映射關系就是你所定義的比例尺。
D3中有各種比例尺函數,有連續(xù)性的,有非連續(xù)性的,在本例子中,你將學到d3.scaleLinear() ,線性比例尺。
使用d3.scaleLinear()創(chuàng)造一個線性比例尺,其中:
let scale = d3.scaleLinear().domain([1,5]).range([0,100])
復制代碼
映射關系:
值得注意的是,上述代碼只是定義了一個映射規(guī)則,映射的輸入值并不局限于domain()中的輸入域。
scale(1) // 輸出:0
scale(4) // 輸出:75
scale(5) // 輸出:100
scale(-1) // 輸出:-50
scale(10) // 輸出:225
復制代碼
于是我們來改造3~4的例子:
let dataset = [1,2,3,4,5];
let svgWidth = 500, svgHeight = 300, barPadding = 5;
let barWidth = (svgWidth / dataset.length);
let svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
let yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, svgHeight]);
let barChart = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("y", d => svgHeight - yScale(d))
.attr("height", d => yScale(d))
.attr("width", barWidth - barPadding)
.attr("transform", (d, i) => {
let translate = [barWidth * i, 0];
return "translate("+ translate +")";
});
復制代碼
然后就會得到以下圖形:
軸是任何圖表的組成部分,本例子中將會用到上面講到的比例尺函數。
let data= [80, 100, 56, 120, 180, 30, 40, 120, 160];
let svgWidth = 500, svgHeight = 300;
let svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
// 首先是拿最大值構建x軸坐標
let xScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, svgWidth]);
// 接下來是反轉值,用作y軸坐標。
let yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([svgHeight, 0]);
// 橫軸的API使用
let x_axis = d3.axisBottom()
.scale(xScale);
// 縱軸的API使用
let y_axis = d3.axisLeft()
.scale(yScale);
// 在svg中提供了如g元素這樣的將多個元素組織在一起的元素。
// 由g元素編組在一起的可以設置相同的顏色,可以進行坐標變換等,類似于Vue中的 <template>
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
let xAxisTranslate = svgHeight - 20;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate +")")
.call(x_axis);
復制代碼
在這里面,你會創(chuàng)建<rect>,<circle>和<line>元素
let svgWidth = 600, svgHeight = 500;
let svg = d3.select("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.attr("class", "svg-container")
let line = svg.append("line")
.attr("x1", 100)
.attr("x2", 500)
.attr("y1", 50)
.attr("y2", 50)
.attr("stroke", "red");
let rect = svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 200)
.attr("height", 100)
.attr("fill", "#9B95FF");
let circle = svg.append("circle")
.attr("cx", 200)
.attr("cy", 300)
.attr("r", 80)
.attr("fill", "#7CE8D5");
復制代碼
let data = [
{"platform": "Android", "percentage": 40.11},
{"platform": "Windows", "percentage": 36.69},
{"platform": "iOS", "percentage": 13.06}
];
let svgWidth = 500, svgHeight = 300, radius = Math.min(svgWidth, svgHeight) / 2;
let svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
//Create group element to hold pie chart
let g = svg.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")") ;
// d3.scaleOrdinal() 序數比例尺
// schemeCategory10, 顏色比例尺
// D3提供了一些顏色比例尺,10就是10種顏色,20就是20種:
let color = d3.scaleOrdinal(d3.schemeCategory10);
let pie = d3.pie().value(d => d.percentage);
let path = d3.arc()
.outerRadius(radius)
.innerRadius(0);
let arc = g.selectAll("arc")
.data(pie(data))
.enter()
.append("g");
arc.append("path")
.attr("d", path)
.attr("fill", d => color(d.data.percentage));
let label = d3.arc()
.outerRadius(radius)
.innerRadius(0);
arc.append("text")
.attr("transform", d => `translate(${label.centroid(d)})`)
.attr("text-anchor", "middle")
.text(d => `${d.data.platform}:${d.data.percentage}%`);
復制代碼
最后,你將學習如何創(chuàng)建折線圖以顯示近四個月的比特幣價格。要獲取數據,你將使用外部API。這個項目還將你在整個課程中學到的很多概念結合在一起,所以這是一個很好的可視化課程結束。
// 外部API,注意日期記得補零
const api = 'https://api.coindesk.com/v1/bpi/historical/close.json?start=2019-03-31&end=2019-07-01';
/**
* dom內容加載完畢時,從API中加載數據
*/
document.addEventListener("DOMContentLoaded", function(event) {
fetch(api)
.then(response => response.json())
.then(data => {
let parsedData = parseData(data);
drawChart(parsedData);
})
.catch(err => console.log(err))
});
/**
* 將數據解析為鍵值對
*/
parseData = data =>{
let arr = [];
for (let i in data.bpi) {
arr.push({
date: new Date(i), //date
value: +data.bpi[i] //convert string to number
});
}
return arr;
}
/**
* 創(chuàng)建圖表
*/
drawChart = data => {
let svgWidth = 600, svgHeight = 400;
let margin = { top: 20, right: 20, bottom: 30, left: 50 };
let width = svgWidth - margin.left - margin.right;
let height = svgHeight - margin.top - margin.bottom;
let svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
let g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let x = d3.scaleTime()
.rangeRound([0, width]);
let y = d3.scaleLinear()
.rangeRound([height, 0]);
let line = d3.line()
.x(d=> x(d.date))
.y(d=> y(d.value))
x.domain(d3.extent(data, function(d) { return d.date }));
y.domain(d3.extent(data, function(d) { return d.value }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
}
復制代碼
以上原實例均來自:Learn D3 for free。
源碼地址:https://scrimba.com/g/gd3js
scrimba是一個非常神奇的網站。它是使用交互式編碼截屏工具構建的。
所有的操作都是:
暫停截屏視頻 → 編輯代碼 → 運行它! → 查看更改
非常值得安利一波。接下來進入第二部分:Vue中使用D3.js的正確姿勢
我們將使用D3和Vue構建一個基本的柱狀圖組件。網上有一堆例子,但我們將專注于寫Vue,而不是濫用D3。
首先,我們需要為項目安裝依賴項。我們可以簡單地安裝和使用D3整庫:
npm i d3
復制代碼
但我在前面講到,實際上D3是幾個分庫的集合,考慮到項目的優(yōu)化,我們只安裝所需的模塊。
使用Vue Cli 初始化項目即可。
因Vue數據響應的特性,我們不需要用到D3操作DOM的那套鏈式創(chuàng)建。
在mounted鉤子中,我們將為窗口調整大小事件添加一個監(jiān)聽器,它將觸發(fā)繪制動畫,并將<svg>大小設置為新窗口的比例。我們不會立即渲染,而是等待300毫秒,以確保完全調整窗口大小。
以下是完整的BarChart.vue,請配合注釋食用:
<template>
<div id="container" class="svg-container" align="center">
<h1>{{ title }}</h1>
<svg v-if="redrawToggle === true" :width="svgWidth" :height="svgHeight">
<g>
<rect
v-for="item in data"
class="bar-positive"
:key="item[xKey]"
:x="xScale(item[xKey])"
:y="yScale(0)"
:width="xScale.bandwidth()"
:height="0"
></rect>
</g>
</svg>
</div>
</template>
<script>
import { scaleLinear, scaleBand } from "d3-scale";
import { max, min } from "d3-array";
import { selectAll } from "d3-selection";
import { transition } from "d3-transition";
export default {
name: "BarChart",
props: {
title: String,
xKey: String,
yKey: String,
data: Array
},
mounted() {
this.svgWidth = document.getElementById("container").offsetWidth * 0.75;
this.AddResizeListener();
this.AnimateLoad();
},
data: () => ({
svgWidth: 0,
redrawToggle: true
}),
methods: {
// 繪制柱形
AnimateLoad() {
selectAll("rect")
.data(this.data)
.transition()
.delay((d, i) => {
return i * 150;
})
.duration(1000)
.attr("y", d => {
return this.yScale(d[this.yKey]);
})
.attr("height", d => {
return this.svgHeight - this.yScale(d[this.yKey]);
});
},
// 調整窗口大小后300毫秒重新繪制圖表
// 即響應式繪制
AddResizeListener() {
window.addEventListener("resize", () => {
this.$data.redrawToggle = false;
setTimeout(() => {
this.$data.redrawToggle = true;
this.$data.svgWidth =
document.getElementById("container").offsetWidth * 0.75;
this.AnimateLoad();
}, 300);
});
}
},
computed: {
dataMax() {
return max(this.data, d => {
return d[this.yKey];
});
},
dataMin() {
return min(this.data, d => {
return d[this.yKey];
});
},
xScale() {
return scaleBand()
.rangeRound([0, this.svgWidth])
.padding(0.1)
.domain(
this.data.map(d => {
return d[this.xKey];
})
);
},
// 通過線性比例尺自動生成
yScale() {
return scaleLinear()
.rangeRound([this.svgHeight, 0])
.domain([this.dataMin > 0 ? 0 : this.dataMin, this.dataMax]);
},
svgHeight() {
return this.svgWidth / 1.61803398875; // 黃金比例
}
}
};
</script>
<style scoped>
.bar-positive {
fill: steelblue;
transition: r 0.2s ease-in-out;
}
.bar-positive:hover {
fill: brown;
}
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 1%;
vertical-align: top;
overflow: hidden;
}
</style>
復制代碼
我們將從父組件App.vue獲取數據:
<template>
<div id="app">
<BarChart title="Bar Chart" xKey="name" yKey="amount" :data="barChartData"/>
</div>
</template>
<script>
import BarChart from "./components/BarChart.vue";
export default {
name: "App",
components: {
BarChart
},
data: () => ({
barChartData: [
{
name: "張三",
amount: 25
},
{
name: "李四",
amount: 40
},
{
name: "老王",
amount: 15
},
{
name: "老賴",
amount: 9
}
]
})
};
</script>
<style>
#app {
font-family: "Open Sans", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #282f36;
margin-top: 30px;
}
</style>
復制代碼
這時候yarn run serve后將會看到:
好像還缺點顯示數值,考慮到該圖高度是根據比例尺生成,我們調整下y坐標:
yScale() {
return scaleLinear()
.rangeRound([this.svgHeight, 0])
.domain([this.dataMin > 0 ? 0 : this.dataMin + 2, this.dataMax + 2]);
},
復制代碼
在AnimateLoad()末尾添加:
selectAll("text")
.data(this.data)
.enter()
復制代碼
最后在<g>元素中添加:
<text
v-for="item in data"
:key="item[xKey].amount"
:x="xScale(item[xKey]) + 30"
:y="yScale(item[yKey]) - 2"
fill="red"
>{{ item[xKey]}} {{ item[yKey]}}
</text>
復制代碼
該庫幾乎憑 Mike Bostock 一人之力完成,且在學術界、專業(yè)團隊中享有極大聲譽。
掌握 D3 后,限制作品水平的只會是想象力而不再是技術。
源碼地址:https://github.com/roger-hiro/d3-bar-chart-vuejs
如果你覺得這篇內容對你挺有啟發(fā),我想邀請你幫我三個小忙:
animate.css是一堆很酷,有趣且跨瀏覽器的動畫,供你在項目中使用。非常適合強調,主頁,滑塊和一般的加水效果。
animate.css v4正在進行許多改進和重大更改,包括CSS自定義屬性支持(又稱CSS變量)和類前綴,以確保安全使用。感興趣的小伙伴可以上github關注進展以及提供反饋!
animate.css的受歡迎程度毋庸置疑,在Github上star數高達接近63k,這是一個非常可觀的數據,我相信其實大多數人或多或少都用過它
https://daneden.github.io/animate.css/
$ npm install animate.css --save
或者 yarn:
$ yarn add animate.css
要在你網站中使用animate.css,只需將樣式表放入文檔的<head>中,然后將動畫類(animated)與任何動畫名稱一起添加到元素中,那么一個簡單的動畫效果就實現了,一下就是一個最簡單的例子:
<head> <link rel="stylesheet" href="animate.min.css"> </head>
<h1 class="animated infinite bounce delay-2s">Example</h1>
以下是你可以使用的所用動畫效果class
可以更改動畫的持續(xù)時間,添加延遲或更改動畫播放的次數:
.yourElement { animation-duration: 3s; animation-delay: 2s; animation-iteration-count: infinite; }
將animate.css與Javascript結合使用時,可以使用animate.css進行大量其他工作。一個簡單的例子:
const element = document.querySelector('.my-element') element.classList.add('animated', 'bounceOutLeft')
還可以檢測動畫何時結束:
const element = document.querySelector('.my-element') element.classList.add('animated', 'bounceOutLeft') element.addEventListener('animationend', function() { doSomething() })
可以使用以下簡單功能來添加和刪除動畫:
function animateCSS(element, animationName, callback) { const node = document.querySelector(element) node.classList.add('animated', animationName) function handleAnimationEnd() { node.classList.remove('animated', animationName) node.removeEventListener('animationend', handleAnimationEnd) if (typeof callback === 'function') callback() } node.addEventListener('animationend', handleAnimationEnd) }
并像這樣使用它:
animateCSS('.my-element', 'bounce') // or animateCSS('.my-element', 'bounce', function() { // Do something after animation })
注意,這些示例使用的是ES6的const聲明,不再支持IE10和某些古老的瀏覽器。
可以直接在元素的class屬性上添加延遲,如下所示:
<div class="animated bounce delay-2s">Example</div>
通過添加這些類,可以控制動畫的速度,如下所示:
<div class="animated bounce faster">Example</div>
Animate.css由gulp.js提供支持,這意味著你可以輕松創(chuàng)建自定義版本。
有些時候你看到別人的網站,感覺速度也不是很快,但是很自然,那么很有可能是使用了動畫,使用動畫不會加快網站的訪問速度,但是可以讓網頁瀏覽器來更加的平滑、更加的自然,使用起來會感覺很舒適,不會給人卡頓的感覺!
文首發(fā)自「慕課網」,想了解更多IT干貨內容,程序員圈內熱聞,歡迎關注!
前端框架發(fā)展到今天,已經出現了很多被廣泛認同的理念,也可以叫作它們的特征。在所有的特征中,最具代表性的特征之一即是數據驅動。
不論 web 應用的內部邏輯如何組織,最終與用戶產生交互的仍然是界面(User Interface,簡稱 UI)。對 web 應用來說,界面則主要由 DOM 元素來呈現。因此,歸根到底,為了讓用戶通過 web 應用完成操作,DOM 元素需要根據實際需要來不斷變化。
例如,用戶通過我們的頁面來查詢當天蘋果的價格,頁面上有一個“查詢”按鈕,還有一個顯示蘋果價格的區(qū)域,此外,在查詢過程中,還有一個加載中的提示。
<button>查詢</button>
<div class="price">今日蘋果價格為 10.00/千克</div>
<div class="loading">加載中</div>
在查詢并顯示結果的過程中,DOM 實際上會經歷這樣幾個狀態(tài):
如果將 1 和 2 使用 CSS 來處理,那么早期我們的 JavaScript 代碼大概是這樣:
// 3. 清空價格區(qū)域的顯示內容
price.innerHTML = '';
// 4. 將加載中的提示顯示出來
loading.style.display = 'block';
// 查詢價格
queryPrice(function(data){
// 5. 待查詢到數據后,將數據拼裝成文字“今日蘋果價格為 10.00/千克”,放入價格區(qū)域中
price.innerHTML = '今日蘋果價格為 ' + data.price + '/千克';
// 6. 將加載中的提示隱藏
loading.style.display = 'none';
});
即使是引入 jQuery 之類的庫,整個過程仍然不會有實質性的變化,主要靠每一行 JavaScript 代碼命令式地修改 DOM 元素來達到修改界面的目的。
既然需要不斷修改界面,而命令式修改界面又如此繁瑣,是否有別的方法來修改界面呢?數據驅動的概念應運而生。它的基本思想是:使用數據來描述應用的狀態(tài),將界面的修改與數據的修改綁定起來,從而實現數據的任何修改都直接反映到界面的修改。
如果用一個公式來表示的話,可以寫成UI=F(state),UI 即指用戶界面,state指應用的狀態(tài),而F則是應用狀態(tài)與用戶界面的映射關系定義。它最直觀的理解是“應用的任何狀態(tài),都可以通過一種確定的映射關系,反映到界面的某種狀態(tài)上”,因此只要狀態(tài)發(fā)生變化,界面也會發(fā)生變化。
上述例子使用 Vue 來編寫:
<template>
<button @click="query">查詢</button>
<div class="price">今日蘋果價格為 {{price}}/千克</div>
<div class="loading" v-show="isLoading">加載中</div>
</template>
<script>
export default {
data(){
return {
price: 0,
isLoading: false
}
},
methods: {
query(){
this.isLoading = true;
queryPrice((data) => {
this.price = data.price;
this.isLoading = false;
});
}
}
}
</script>
在上述代碼中,應用的狀態(tài)就是data,包括兩個值price和isLoading。我們的邏輯代碼只需要對這兩個值進行操作,即可以完成整個應用功能的界面變化。
而狀態(tài)和界面的映射則由<template>來定義,例如今日蘋果價格為 {{price}}/千克表示狀態(tài)中的price變量需要顯示在此處,v-show="isLoading"則表示是否顯示加載中完全由變量值isloading決定。
事實上當前前端主流的幾大框架,在界面的渲染上都不約而同選擇了數據驅動的方式,深入理解這一模式有助于我們更好地理解前端框架。
數據驅動界面變更能迅速被廣泛接受,也可以從側面說明它確實為開發(fā)者帶來了一些好處。
首先,因為開發(fā)者僅需要管理數據,使得關于界面細節(jié)控制的代碼不再需要開發(fā)者編寫。同時,由于狀態(tài)被抽象出來,同一個變量值在界面上的多處變化全部由映射關系來決定,而不需要開發(fā)者手工修改每一處變化。這兩者結合起來使得開發(fā)者的心智負擔大大減少,需要關注的代碼量也大大減少,從而使得開發(fā)效率得以大幅提升,出現 bug 的概率也大大減少。
其次,專門將應用狀態(tài)抽象出來,使得開發(fā)者必須認真思考代碼的組織方式,而因為界面相關細節(jié)的消失,大部分的代碼都變成了邏輯代碼,使得傳統(tǒng)編程中的模式都可以被應用到前端代碼中,從而使得前端代碼能夠支持更大規(guī)模的應用,也能更好地組織前端代碼本身,使得代碼更容易閱讀和維護。
狀態(tài)的抽象也使得開發(fā)者可以精準地保存和還原任意一個界面狀態(tài)。因為界面的每一時刻的界面表現都是由這一時刻的應用狀態(tài)決定,因此只要能夠將此時的應用狀態(tài)進行保存,就能在另一個時間、空間中重現應用此時的界面表現。
這個特性在某一些場景下非常好用,例如線上 bug 的排查。如果我們有辦法取到用戶的當前狀態(tài),就有辦法完全還原用戶的界面表現,從而快速復現應用碰到的 bug,而不用再苦苦和用戶溝通詳細的操作步驟,一點點地確認應用可能是哪里出了問題。除此之外,這個特性還可以用于實現“時間旅行”效果,即應用界面的回放。我們只需要將狀態(tài)的變更都記錄下來,就能看到應用從初始化一直到最終狀態(tài)中間發(fā)生的完整事情。它本身可以作為一個效果來使用,也可以用來支持一些功能(例如撤銷/重做)。
因為應用界面完全由應用狀態(tài)決定,而狀態(tài)映射到界面的操作一般由框架來幫忙我們完成,因此在測試的時候,就有機會將重點放在狀態(tài)的測試上。即在很多情況下,我們只需要測試邏輯和數據,確保應用狀態(tài)是正確的,即可大概認為界面是正確的。
因為界面測試的成本要遠高于邏輯和數據測試,如果我們能在不做界面測試的情況下也保證應用邏輯和狀態(tài)是正確的,將大大提升測試效率。
時至今日,數據驅動已經成為絕大部分前端開發(fā)者的共識,Vue 也是這一理論的實踐者,后面我們將看到 Vue 是如何實現這一重要特征的。
歡迎關注「慕課網」,發(fā)現更多IT圈優(yōu)質內容,分享干貨知識,幫助你成為更好的程序員!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。