// 通过audio标签var sound, audio = new Audio();audio.addEventListener('canplay', function() { sound = context.createMediaElementSource(audio); sound.connect(context.destination);});audio.src = '/audio.mp3'; var v =document.getElementsByTagName('video')[0];var aCtx = new (window.AudioContext || window.webkitAudioContext)();var source = aCtx.createMediaElementSource(v); // 创建声音源处理器// var sourceNode = aCtx.createBufferSource();// 创建声音终点处理器// var destinationNode = aCtx.destination; // 创建声道分离处理器 此处的2是声道数 可以从a中获取var splitterNode = aCtx.createChannelSplitter(2); // 源连接声道分离处理器source.connect(splitterNode); // 创建左右声道频谱分析器var leftAnalyserNode = aCtx.createAnalyser();var rightAnalyserNode = aCtx.createAnalyser(); splitterNode.connect(leftAnalyserNode, 0)splitterNode.connect(rightAnalyserNode, 1) // 合并声道var mergerNode = aCtx.createChannelMerger(2);leftAnalyserNode.connect(mergerNode, 0, 0)rightAnalyserNode.connect(mergerNode, 0, 1)mergerNode.connect(aCtx.destination) // 获取左右声道频谱数据var leftBuff = new Uint8Array(leftAnalyserNode.frequencyBinCount);leftAnalyserNode.getByteTimeDomainData(leftBuff) var rightBuff = new Uint8Array(rightAnalyserNode.frequencyBinCount);rightAnalyserNode.getByteTimeDomainData(rightBuff) // 字节值的范围介于0-255之间，是的，映射到-1到+1，因此128为零。 （它不是伏特，而是全范围无单位值。） var rightBuff = new Float32Array(rightAnalyserNode.frequencyBinCount);rightAnalyserNode.getFloatFrequencyData(rightBuff) // 单位dB // 修改声音大小// 创建音量节点 var gainNode = aCtx.createGain();source.connect(gainNode);gainNode.connect(aCtx.destination);gainNode.gain.value=0.1 // 获取音量scriptProcessor = aCtx.createScriptProcessor(4096,2,2);source.connect(scriptProcessor);scriptProcessor.connect(aCtx.destination);// 开始处理音频scriptProcessor.onaudioprocess = (e) => { // 获得缓冲区的输入音频，转换为包含了PCM通道数据的32位浮点数组 const lBuffer = e.inputBuffer.getChannelData(0); const rBuffer = e.inputBuffer.getChannelData(1); // 获取缓冲区中最大的音量值 const lMaxVal = Math.max(...lBuffer); const rMaxVal = Math.max(...rBuffer); // 左右显示音量值 const lVol = Math.round(lMaxVal 100); const rVol = Math.round(rMaxVal 100); console.log(lVol,rVol);};不需要上面那坨左右声道的时候，可以直接这样设置： oAudio = document.getElementById('shakedown-audio-mp3'); // 创建音频上下文对象 oCtx = new AudioContext(); // console.log(oCtx); // 创建媒体源,除了audio本身可以获取，也可以通过oCtx对象提供的api进行媒体源操作 audioSrc = oCtx.createMediaElementSource(oAudio); //通过标签获取 //audioSrc = oCtx.createMediaStreamSource(stream); //使用多媒体流 // 创建分析机 analyser = oCtx.createAnalyser(); // 媒体源与分析机连接 audioSrc.connect(analyser); // 输出的目标：将分析机分析出来的处理结果与目标点（耳机/扬声器）连接 // analyser.connect(oCtx.destination);// 连接后会输出声音，不连接无声只获取音量 let voiceHeight = new Float32Array(analyser.frequencyBinCount); analyser.getFloatFrequencyData(voiceHeight); 这里主要是用了createScriptProcessor这个api，但是此api即将废弃。另外一种方式： // 获取音频分贝 this.audioContext = new (window.AudioContext || window.webkitAudioContext)() this.$refs.outputAudioWrap.innerHTML = ` ` const audio = document.getElementById('outputAudio') // audio.src = require('@/assets/media/test_output.wav') audio.src = require('@/assets/media/squirrel.mp3') audio.loop = true audio.play() // 获取用户的 media 信息 // 将音频的声音输入这个对象 // 创建媒体源,除了audio本身可以获取，也可以通过audioContext对象提供的api进行媒体源操作 this.outputSource = this.audioContext.createMediaElementSource(audio) // 创建分析机 this.outputAnalyser = this.audioContext.createAnalyser() this.outputAnalyser.fftSize = 128 // 默认1024 // 媒体源与分析机连接 this.outputSource.connect(this.outputAnalyser) // 输出的目标：将分析机分析出来的处理结果与目标点（耳机/扬声器）连接 this.outputAnalyser.connect(this.audioContext.destination)// 连接后会输出声音，不连接无声只获取音量 this.getOutputVolume(this.outputSource) getOutputVolume () { var bufferLength = this.outputAnalyser.frequencyBinCount var dataArray = new Float32Array(bufferLength) this.outputAnalyser.getFloatFrequencyData(dataArray) let maxVolume = -Infinity for (let i = 4, ii = dataArray.length; i < ii; i++) { if (dataArray[i] > maxVolume && dataArray[i] < 0) { maxVolume = dataArray[i] } } let audioVolume = Math.round(Math.pow(10, maxVolume / 85) * 20) audioVolume = audioVolume < 0 ? 0 : audioVolume audioVolume = audioVolume > 10 ? 10 : audioVolume audioVolume = audioVolume * 10 audioVolume = audioVolume / 100 * this.outputVoiceSize this.outputVolume = audioVolume // 另外一种平滑的方式。 // var dataArray = new Uint8Array(bufferLength) // this.outputAnalyser.getByteFrequencyData(dataArray) // console.log('=====================') // const max2 = Math.max.apply(null, dataArray) // const middleIndex = Math.ceil(dataArray.length / 2) // const middleNum = Math.ceil(dataArray[middleIndex]) // const avgNum = dataArray.reduce((previous, current) => previous + current, 0) / dataArray.length // const percent = middleNum / max2 // console.log('percent', percent) // let volumeNum = percent * this.outputVoiceSize // volumeNum = volumeNum > 100 ? 100 : volumeNum // volumeNum = volumeNum ? volumeNum : 0 // this.outputVolume = volumeNum // console.log('this.outputVolume', this.outputVolume) this.outputRequestId = requestAnimationFrame(this.getOutputVolume) },
Ⅰ. 什么是eventBus？通俗的讲，就是在任意一个组件，想把消息（参数） -> 传递到任意一个组件 ，并执行一定逻辑。Ⅱ. vue3 如何使用 eventBusvue3中没有了，eventBus，所以我们要自己写，但是非常简单。步骤一 （eventBus 容器）在src目录，创建个bus文件夹，存放 自己写的 bus.js ；编写 bus.js => 在class 原型上绑定三个 （订阅，取消订阅，发布）函数；// bus.jsclass Bus {constructor() { this.list = {}; // 收集订阅 } // 订阅 $on(name, fn) { this.list[name] = this.list[name] || []; this.list[name].push(fn); } // 发布 $emit(name, data) { if (this.list[name]) { this.list[name].forEach((fn) => { fn(data); }); } } // 取消订阅 $off(name) { if (this.list[name]) { delete this.list[name]; } }}export default new Bus;订阅者（on），讲函数放入 list 中 => 等待发布者（emit），传参去调用；由于函数是对象，内存地址未发生变化，还在在订阅者（on）组件中执行。步骤二 （ 订阅者 ）在 comA.vue 中订阅；订阅只是 存放函数，并未执行，等发布后才会执行； {{ name }} --- {{ age }} 在离开组件（onUnmounted）时 ，将注册进去的 ，订阅函数的数组删除，避免存放越来越多。步骤三 （ 发布者 ）在 comB.vue 中发布；调用订阅 并传参； 发布 发布后，在订阅者的组件就会执行，注意对应的 订阅和发布的name 要相同。
你的 JavaScript 是否曾经无法正确执行，并且你很难找出原因？你的 CSS 是否曾经表现得很奇怪，并且你很难找出原因或让它按预期显示？调试是编程的基本组成部分，也是所有软件开发人员的基本技能。当你构建网站和应用程序时，你很快就会意识到调试会占用大量时间。因此，学习任何可以帮助你更有效地对 Web 应用程序进行故障排除的工具的基础知识和高级功能将使你成为更好的调试器。要成为更好的调试器，你必须熟悉使调试更容易的正确工具。本文重点介绍使用 Chrome DevTools 更轻松地对 Web 应用程序进行故障排除的一些最佳做法、关键功能和提示。什么是谷歌浏览器开发工具？谷歌浏览器可能是开发人员中使用最广泛和最受欢迎的网络浏览器，因为它与众不同。它已逐渐成为大多数开发人员测试和调试其网站和 Web 应用程序的首选 Web 浏览器。Google Chrome 为开发人员提供了使用浏览器中的内置开发人员工具（又名 DevTools）调试大多数 Web 应用程序的能力，而无需第三方调试工具。Google Chrome 的开发人员工具中有多种功能和工具可供开发人员直接在浏览器中编辑代码、测试和添加断点以检测问题，并更有效地调试代码。DevTools 面板的快速视图要访问 DevTools，请按 Control + Shift + C（在 Windows 或 Linux 上）和 Command + Option + C（在 Mac 上）。此外，你可以右键单击网页的任何部分，选择“检查元素”，或通过选项菜单访问它。 Google 的 Chrome DevTools 窗口在迷你窗口中打开时会在顶部显示一些快速选项卡，以及一些额外的隐藏选项卡，可通过单击它们旁边的 >> 符号或切换到全屏视图来访问这些选项卡。每个小组的职责的简要总结：Elements：在这里可以检查和编辑 DOM 节点和样式属性。Console：可以通过控制台查看和运行 JavaScript 代码。Sources：源代码可在此处获得。你可以调试 JavaScript 代码并添加断点等。Network：调试网络相关的活动。你可以查看和监控网络。Performance：分析速度并优化性能。Memory：通过跟踪内存使用情况来修复与内存相关的问题。Application：可以查看sessionStorage、localStorage、IndexDB、cookies，以及其他应用相关的数据文件。Security：调试证书问题和其他安全问题。Lighthouse：让你审核应用程序的性能、可访问性、SEO 等。monitor(function) - 监控一个函数Chrome 中提供了一些控制台实用程序 API，它们为常见的调试任务提供了方便的功能。顾名思义，monitor() 函数是此类控制台函数之一，用于监视特定函数以了解何时调用该函数以及在调用该函数时将哪些参数传递给该函数。目标函数必须作为参数传递给 monitor() 函数，以便它观察任何调用的指定函数。一旦被调用，就会立即将一条消息记录到控制台，其中包含函数名称及其参数，表明该函数已被调用。 使用该unmonitor()功能将取消对该功能的监控。monitorEvents() - 监控 DOM 对象事件此函数用于监视 DOM 中的对象是否有特定事件或事件。当在指定对象上触发此事件时，该函数立即将事件和对象输出到控制台。当指定对象上发生任何指定事件时，Event 对象将被记录到控制台。要监视的事件可以是特定事件、事件数组或映射到预定义集合的通用事件“类型”。此函数接收应监控的对象和特定事件，即monitorEvents(object [, events]). 例如，要监视窗口对象的任何调整大小事件：monitorEvents(window, "resize"); 输出： 你可以选择性地停止监视对象 ie 上的特定事件unmonitorEvents(object[, events])，或对象 ie 上的所有事件unmonitorEvents(window)。要了解有关此功能的更多信息，请访问文档。table() - 将数组输出为表从数据库或外部 API 获取数据时，它通常以对象数组的形式出现。想象一下，你正试图在你的 chrome 控制台中预览或读取此返回的数据，以找出在你的应用程序中不起作用的内容。该console.log()函数通常将其显示为难以阅读或分类的文本输出。你显然希望以更易于阅读的有条理的方式查看所有项目、属性及其相关属性。你可以在 chrome 控制台中使用table()函数即 table(data) 来完成此操作。这种方法以更人性化、更易于阅读和理解的表格格式打印和组织信息。例如：const users = [ { first: "zhang", last: "san"}, { first: "li", last: "si", birthday: "19990919" }, { first: "wang", last: "wu",}, ]; table(users); 注意：使用 可以实现类似的结果console.table()。getEventListeners() - 获取事件监听器使用作为参数传递给它的 DOM 对象调用 getEventListener 函数会返回在该特定对象上注册的所有事件。返回的值是一个对象，其中包含每个注册的事件类型（如点击、按键等）的数组。每个成员数组都包含该事件类型的所有事件，并且可以扩展以探索它们各自的属性，例如它们触发的关联函数。debug() - 调试函数为了修复不符合预期的页面、功能或组件，你通常会添加一个“调试器”；声明函数声明以激活调试器并尝试了解它有什么问题。但是，这需要你手动进入源代码以添加这些调试器语句。Chrome 允许你直接从控制台执行此操作，而无需使用 debug() 函数访问你的源代码。只需在控制台中调用它并将函数的名称传递给它，它就会自动将调试器注入到函数中，让你可以单步调试代码。 在控制台中调用debug(functionReference)会增加一个调试器；引用函数声明的第一行上的语句。DevTools 还提供断点，让你逐行执行代码。你可以通过单击“源”面板中的行号来添加它们。左键单击会自动添加断点，而右键单击数字可以设置条件断点，这对于调试循环非常有用。 要禁用函数中的所有断点，请使用undebug(functionReference)或使用源面板。调试期间重启帧借助 Chrome 新添加的 DevTools Restart Frame 功能，你可以在调试函数时遇到断点后重新运行前面的代码。这意味着你可以在函数中某处暂停后立即重新运行前面的代码。因此，你可以在当前调用堆栈中穿越时间（尽管在有限的范围内）。注意：当你使用 Restart Frame Chrome 时，状态不会恢复。执行指针简单地移动到函数的顶部。在 DOM 元素上添加断点（属性/模板更改）有时你可能需要在有条件或动态更改的 DOM 元素上放置断点或调试器。你可以通过右键单击目标 DOM 元素并选择“中断”->“属性修改”来轻松修改 DOM 元素。此外，你还可以尝试其他一些选项。代码片段在调试时，你可能经常需要一遍又一遍地执行同一行代码，直到你完成故障排除。但是，这会导致你进行大量重复输入或不断地从你最喜欢的代码编辑器复制并粘贴到你的 Chrome 浏览器控制台。Google Chrome DevTools 有一个代码片段功能，允许你保存代码片段，以便在需要时使用。片段是一段 JavaScript 源代码，你可以保存并重复使用以进行调试。创建的代码片段可以在任何时候在任何网站上的每个调试会话中通过单击按钮来执行，因为它会永久保存，直到手动删除。这可以节省你在每个页面测试中输入重复信息的时间。在 Chrome 中创建代码片段：1.打开 DevTools 窗口并单击选项卡面板中的“Sources”。 2.单击>>符号，选择“snippets”，然后选择 New snippet。 3.给它一个名字并写下你的代码片段。 4.保存文件。 5.现在，你可以通过右键单击片段文件名并选择“运行”在任何网页上执行代码。console.log('Hello,嘎嘎'); document.body.innerHTML = ''; var p = document.createElement('p'); p.textContent= 'Hello,嘎嘎'; document.body.appendChild(p); 结论毫无疑问，Chrome DevTools 是对开发者工具箱的极好补充。在这篇文章中，我们研究了如何通过使用 Google Chrome 的 DevTools 直接在浏览器中进行调试来提高调试技能。希望这将使你能够更高效地对 Web 应用程序和组件进行故障排除。尽管如此，这只是 Google Chrome 的 DevTools 中提供的众多功能的一小部分。你还使用哪些其他工具和技巧？可以在下面的评论区告诉我。
Vue3基础选项式 API组件的初始化还是和Vue2那样 当前鼠标位置 x: {{ mouse.x }} y: {{ mouse.y }} 当前点击次数：{{ count }} 点击 组合式APIeg： 当前鼠标位置 x: {{ mouse.x }} y: {{ mouse.y }} 当前点击次数：{{ count }} 点击 代码抽离—hooks 钩子函数import { ref, reactive, onMounted, onUnmounted } from 'vue' // 抽离---hooks 钩子函数 // 业务 A const useCount = () => { const count = ref(0) const add = () => { count.value++ } onMounted(() => { // ❗✅多个生命周期，不会被合并 console.log('业务 A 生命周期') }) return { count, add } } // 业务 B const useMouseMove = () => { const mouse = reactive({ x: 100, y: 100 }) const mouseMove = e => { mouse.x = e.clientX mouse.y = e.clientY } onMounted(() => { document.addEventListener('mousemove', mouseMove) }) onUnmounted(() => { document.removeEventListener('mousemove', mouseMove) }) return { mouse } } export default { setup() { const { count, add } = useCount() const { mouse } = useMouseMove() return { count, add, mouse } } } Vue3生命周期图示 ❗✅多个生命周期，不会被合并setup 函数❗setup 函数是 Vue3 特有的选项，作为组合式API的起点，注意以下三点：从组件生命周期看，它在 beforeCreate 之前执行函数中 this 不是组件实例，是 undefined✨如果数据或者函数在模板中使用，需要在 setup 返回经验：今后在vue3的项目中几乎用不到 thisreactive 函数只能用于复杂类型数据，数组类型也不建议用1. 导入 reactive 函数import { reactive } from 'vue' 2. 通过 reactive 函数包装成响应式对象const obj = reactive({ name:'zs', age:18 }) const btn = () => { obj.age++ } 3. 返回给模板使用return { obj,btn } 组合式API-ref函数ref 函数，可以把任意类型数据转换成响应式。从 vue 中导入 ref 函数在 setup 函数中，使用 ref 函数包装成响应式数据(不分类型)最后 setup 函数返回一个对象，包含该响应式数据即可❗使用 ref 的两个注意点： computed 函数导入 computed 函数 import {computed} from 'vue'调用 computed 函数并传入一个回调函数 const computedRes = computed(() => { return xx})函数内部记得 return 返回结果const nextAge = computed(() => { return age.value + 1})computed 函数的另一个写法const nextAge2 = computed({ get() { return age.value + 2 }, set(value) { age.value = value - 2 } }) watch 函数watch 参数被监听的数据回调函数配置项(可选)基本写法基础类型import { ref,watch } from 'vue' const money = ref(1400) watch(money,() => { console.log('钱变化了', money.value) }) 复杂类型const arr = JSON.parse(localStorage.getItem('todoVue3')) || [ { id: 1, content: 'eat', done: true }, { id: 2, content: 'play', done: false }, ] const list = ref(arr) watch(list,() => { localStorage.setItem('todoVue3', JSON.stringify(list.value)) },{ deep:true, immediate:true } ) 进阶写法基础类型import { ref,watch } from 'vue' const money = ref(1400) watch(money,(newVal,oldVal) => { console.log('钱变化了',newVal,oldVal) }) 复杂类型不常用，✨工作经验：一般是一个watch 监听一个值，监听对象属性的话建议监听整个属性（但是监听整个对象newValue 此处和 oldValue 是相等的，因为它们是同一个对象）通过 getters 函数写法，可以侦听对象的某个属性通过数组，可同时侦听多个数据watch( [()=> obj.value.age,() => obj.value.cp.age], (newVal,oldVal){ console.log('obj变化了',newVal,oldVal) }, { deep:true } ) 模板ref获取DOM元素创建 ref const hRef = ref(null)模板 ref 建立关联 组件挂载完毕后可以访问 DOM节点 onmounted { hRfe.value }应用场景//需求：获取输入框焦点 const iptRef = ref(null) const focusClick = () =>{ iptRef.value.focus() } // 需求：获取输入框焦点 audioRef.value.play() ❗获取组件的 DOM 元素的注意点父组件想使用子组件的方法需要子组件用defineExpose主动暴露方法代码实现子const sayHi = () => { console.log('sayHi~') } // 暴露 方法 defineExpose({ sayHi }) 父// script const footerRef = ref(null) const clickBtn = () => { sonRef.value.sayHi() } // template <Son ref="sonRef"/> 父传子基本结构在 script 中用defineProps获取 props// father: const money = ref(100) <Children :money="money"> // son： 子传父 defineEmits父<ChildCom @change-money="changeMoneyFn" :money="money"></ChildCom>子定义 emit ，显式声明事件名称const emits = defaultEmits(['changeMoney']) const btn = () => { emit('changeMoney',100) // ❗注意没有 this } // @click="emits('changeMoney')" 跨组件通讯provide 提供数据每个 provide 提供一个数据，可重复使用provide('count',count)inject 获取数据每个 inject 接收一个数据，可重复使用const count = inject('count')辅助函数toRefsconst user = reactive({ name:'zg',age:80 })const res = toRfes(user)把 obj 的每一个属性都变成独立的 ref 对象，所以解构出来的每个属性都是响应式的 refconst { name,age } = toRefs(user)核心库vue-router基础使用安装 npm i vue-router@4 --save导入 创建路由实例的函数 和 创建哈希模式（createWebHashHistory ）历史模式（createWebHistory） 的函数import { createRouter,createWebHashHistory } form 'vue-router' // 创建路由实例 const router = createRouter({ // vue2 mode:'hash' history:createWebHashHistory, routes:[] }) export default router 挂载路由：main.jsimport router from './router' // 以插件形式使用 router const app = createApp(App) app.use(router) app.mount('#app') 路由配置和路由组件在路由实例的 路由表routes: [ { path: '/home', component: () => import('../views/HomePage.vue') }, { path: '/login', component: () => import('../views/LoginPage.vue') }, ], 路由链接与路由出口<router-link> <router-view>vue-router-组合式API因为我们在 setup 里面没有访问 this，所以我们不能再直接访问 this.$router或 this.$route使用 useRouter 和 useRoute 函数：import { useRouter,useRoute } from 'vue-router'获取全局路由实例const router = useRouter()获取当前路由信息const route = useRoute()使用：router.push('/home')route.query/route.path/route.fullPath/route.params ✨非组件跳转页面方法：❌useRouter 使用有限制，只能使用于 setup 组合式 API 内// 非组件中，可通过导入路由实例，用于实现页面跳转 import router from '@/router'; // const router = useRouter(); // console.log(22, router); // undefined // router.push('/'); // ✅非组件直接导入路由实例 import router from '@/router'; router.push('/'); piniaPinia 是 Vue.js 的轻量级状态管理库官方网站中文文档(非官方,翻译了大部分内容)为什么学习pinia?pinia和vuex4一样，也是vue官方的状态管理工具(作者是 Vue 核心团队成员）pinia相比vuex4，对于vue3的兼容性更好pinia相比vuex4，具备完善的类型推荐pinia同样支持vue开发者工具,最新的开发者工具对vuex4支持不好Pinia 的 API 设计非常接近 Vuex 5 的提案。基础使用安装 npm i pinia@2 --save在 main.js中挂载import { createPinia } from 'pinia' app.use(createPinia()) pinia 的 store三个概念，state、getters 和 actions，可以假设这些概念相当于组件中的 data、 computed 和 methods。✨pinia没有mutation，用起来更加方便pinia 支持两种 API 风格：defineStore('唯一标识',选项式API配置)defineStore('唯一标识', 组合式API函数)返回值是一个用于获取 store 的函数建议命名规范：useXxxStoreexport const useCounterStore = defineStore('counter',{ state:() => { return { count:0 }} }) 使用import { useCounterStore } from './store/counter' // 调用获取 store 的函数 const counterStore = useCounterStore() {{ counterStore.count }} 选项式API写法state:()=>{ // 建议 vue2、vue3 都在state return 一个对象 return { count:100 } } // getters 看做 computed getters: { doubleCount() { // ❗❗不用再写state，通过 this 使用即可 return this.count * 2 }, }, // 使用 {{ counterStore.doubleCount }} // actions 看做 methods，支持同步和异步，传参也很方便 actions: { // 支持同步 addCount(val) { this.count += val }, // 支持异步 addCountASync() { setTimeout(() => { this.count++ }, 1000) }, }, // 使用 @click="couterStore.addCount(10)" ❗state 与 getters 同名，以 getters 的为准组合式API写法import { defineStore } from 'pinia' import { computed,ref } from 'vue' export const useMoneyStore = defineStore('money',()=>{ // state const salary = ref(1400) // getters const doubleSalary = computed(()=> saraly.value*2) // actions const addSalaryAsync = () =>{ setTomeout(()=>{ salary.value++ },1000) } // actions const addSalary = (val)=>{ salary.value += val } return {salary,doubleSalary,addSalaryAsync} }) 辅助函数storeToRefs简化使用 store❌解构出的方法用不了 ✅✨工作经验：不使用 storeToRefs，直接使用 toRefs 就行，只不过官网上有，介绍一下而已import { storeToRefs } from 'pinia' import { useMoneyStore } from './store/money' const moneyStore = useMoneyStore() ❌直接解构数据会失去响应式 // const { salary,doubleSalary } = moneyStore ✅storeToRefs 解构后还支持响应式 const { salary,doubleSalary } = storeToRefs(moneyStore) 使用：{{ salary }} {{ doubleSalary }}合并模块管理从合并管理的模块中导入所需的 storesrc/store/index.js// 导出模块合集 export * from './modules/counter' export * from './modules/money' App.vueimport { useCounterStore,useMoneyStore } from './store'不用分别从 ./store/counter、 ./store/money中导入TodoList案例store return 回去时记得＋{ } …未完待续
【中秋快乐】如何用three.js实现我的太空遐想3D网页
目录创作背景功能分解创建3d地球创建3d月球 创建3d小火箭设计地球自转轨迹和月球、小火箭旋转的行星轨道1.地球的自转轨迹2.月球的公转轨迹3.小火箭的公转轨迹最终效果展现 彩蛋*创作背景 马上又是一年中秋佳节到来之际，中秋佳节自古以来就是团圆，思乡的节日，佳节当晚一边赏着月色，一边品尝着美味的月饼，和亲人好友诉说着想念之情 ，悠闲惬意之情油然而生。 古人有诗云：”明月几时有，把酒问青天，不知天上宫阙，今夕是何年？“ 这是古人对月亮和太空的理解，那么当现代的我们抬头望向星空，望向月亮的时候，你的太空遐想是什么呢？正好博主最近也在研究3维方面技术栈，来吧，这篇文章会从0和你一起用three.js 来实现：你心目中的地球和月球，并且在漫天的繁星中画出行星运转轨迹，并保留有一个小彩蛋哦*功能分解3d地球3d月球3d小火箭地球自转轨迹 和 月球、小火箭旋转的行星轨道彩蛋*创建3d地球如何实现一个3d地球呢第一步：我们首先需要创造一个三维的空间场景 /* * 实例化场景 */ scene = new THREE.Scene(); three.js封装好了三维场景实例化的方法，通过new THREE.Scene()，我们就可以创建了一个三维场景第二步：我们向三维场景里面加一个摄像机，方便我们查看三维场景中的内容 /** * 场景添加相机 */ var width = window.innerWidth; //窗口宽度 var height = window.innerHeight; //窗口高度 camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 ); //透视相机 camera.position.z = 15; scene.add( camera ) 相机概念示意图第三步：创建我们的地球啦，先使用new THREE.SphereGeometry(EARTH_RADIUS, 32, 32)创建一个球体，再给咱们的球体贴上地球地图的材质，这里博主已经收集了，一并放在工程目录上，阅读完本文后，可以下载使用，最后再使用new THREE.Mesh(earthGeometry, earthMaterial1)方法，这样一个地球就创建成功啦材质图： /** * 场景中添加地球 */ const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 32, 32); //球体创建方法 const earthMaterial1 = new THREE.MeshPhongMaterial({ map: new THREE.TextureLoader().load('./asset/img/textures/planets/earth_atmos_2048.jpg'), // 添加纹理 shininess: 5, }) earth = new THREE.Mesh(earthGeometry, earthMaterial1) //网格模型对象Mesh earth.position.set(0, 0, 0) earth.castShadow = true; // 光源有阴影 earth.receiveShadow = true scene.add(earth); //创建地球 第四步：执行threejs的渲染函数requestAnimationFrame() 这个频率为每秒60帧，让我们的场景实时动起来创建3d月球创建3d月球的方法步骤和3d地球类似，这里就不重复概述啦，将成功创建后的3d月球场景p给大家看一下 创建3d小火箭因为3d火箭的模型结构较为复杂，这里我们直接使用three.js官方提供的模型文件来导入场景，方法如下： /** * 场景中添加飞船 （飞船造型太复杂，代码是画不出来滴 直接导入模型） * */ const loader = new LWOLoader(); loader.load('asset/model/lwo/Objects/LWO3/Demo.lwo', function(object) { rocket = object.meshes[2]; //这个模型有三部分 火箭是第三个 rocket.position.set(0, 0, 0); rocket.scale.set(0.5, 0.5, 0.5); // rocket.rotateZ(MathUtils.degToRad(-90)) scene.add(rocket); }); 这三个场景，博主已经分布写成三个demo，大家后续clone下来自行查看设计地球自转轨迹和月球、小火箭旋转的行星轨道刚刚我们都已经实现了模型的创建和加载，这里我们把这个三个模型加在一个场景里面，再给每个模型设计一下自己的运行轨迹，这样就可以完成我们的地球-飞行器-月亮的太空运行轨迹遐想啦1.地球的自转轨迹通过学习three.js的官方文档可知，three.js提供了rotateOnAxis 方法，这个方法可以在局部空间中绕着该物体的轴来旋转一个物体 ，使用方法如下，传入两个参数( axis : Vector3, angle : Float )坐标轴和弧度，我们可将地球设置为围绕Y轴选择，转的角度为圆周率的等分点即可earth.rotateOnAxis(new Vector3(0,1,0),3.14) 2.月球的公转轨迹这个就比较复杂，月球需要围绕着地球来旋转，这个运行轨迹在空间中是个圆，在官方文档学习示例中，我们可以看到月球的运动轨迹就是通过修改月球在三维坐标系中的x和z值来实现的，也就是这行关键代码：moon.position.set(Math.sin(elapsed) * 5, 0, Math.cos(elapsed) * 5); x轴坐标：Math.sin(elapsed)*5 y轴坐标：Math.cos(elapsed)*5球的运动轨迹，是围绕y轴，在xoz形成的平面内画圆。通过以上观察，在运动轨迹上，任一点的坐标x，z坐标满足一下规律：也就是三角函数的正弦余弦平方和为1，即半径为1。3.小火箭的公转轨迹火箭的公转轨迹也设计的和月球相同，后续有想法的小伙伴，可以写写更为复杂的数学公式，实现更多不同的运动轨迹。最终效果展现最后我们将全部元素组装到一个场景里面，就可以实现如下的效果，怎么样，还不错吧
前言JWT(全称：Json Web Token)是一个开放标准(RFC 7519)，它定义了一种紧凑的、自包含的方式，用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任，因为它是数字签名的。简单业务流程JWT组成Jwt由 Header.Payload.Signature 三部分数据拼接组成eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5leGFtcGxlcy5jb20iLCJpYXQiOjE2NTM0ODI5NjAsImV4cCI6MTY1NDA4Nzc2MCwibmJmIjoxNjUzNDgyOTYwLCJqdGkiOiIyMjdjYTFmZi03ZGE1LTQzMGUtOTFjMy05ZTQxMDdjNWQ0YTgiLCJzdWIiOjF9.6LXlvsswMsKJpHD3ChsSGOzEJnYwm6eF212JE7Uc4p4 Header示例描述JWT元数据的Json对象,一般情况如下{ typ: "JWT", alg: "HS256", } 解码header// header数据为上面示例的第一部分 const header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; console.log(Buffer.from(header, "base64").toString("utf8")); // 输出：{"alg":"HS256","typ":"JWT"} Payload示例描述JWT元数据的Json对象,一般情况如下{ // 发行方 iss: "https://www.examples.com", // 签发日期 iat: 1653482960, // 到期日期 exp: 1654087760, // 不可用日期（在此之前） nbf: 1653482960, // JWT ID用于标识该JWT jti: "227ca1ff-7da5-430e-91c3-9e4107c5d4a8", // subject的缩写不知道该如何翻译，不过一般存储的都是 用户ID sub: 1 } 解码Payload因为payload仅为base64加密，很容易解码。所以不要在Payload中存储敏感信息。// payload数据为上面示例的第一部分 const payload="eyJpc3MiOiJodHRwczovL3d3dy5leGFtcGxlcy5jb20iLCJpYXQiOjE2NTM0ODI5NjAsImV4cCI6MTY1NDA4Nzc2MCwibmJmIjoxNjUzNDgyOTYwLCJqdGkiOiIyMjdjYTFmZi03ZGE1LTQzMGUtOTFjMy05ZTQxMDdjNWQ0YTgiLCJzdWIiOjF9"; console.log(Buffer.from(payload, "base64").toString("utf8")); // 输出：{"iss":"https://www.examples.com","iat":1653482960,"exp":1654087760,"nbf":1653482960,"jti":"227ca1ff-7da5-430e-91c3-9e4107c5d4a8","sub":1} Signature因为本文模拟数据中 Header 中的 alg 为 Hs256 所以，加密使用 HMAC-SHA256 作为签名算法// 秘钥 此秘钥不要泄漏。一般存放在后端的配置文件汇总 const secret = "lbOAmnKu9xpvjEz7TDUFLf2RXMCw1NeGBo04ksgWQidJy3SVPcH85atZhYrI6qxx"; // header头，与前文设置一样，进行base64加密 const header = Buffer.from( JSON.stringify({ alg: "HS256", typ: "JWT", }), "base64" ).toString("utf8"); // 载荷，与前文设置一样，进行base64加密 const payload = Buffer.from( JSON.stringify({ iss: "https://www.examples.com", iat: 1653482960, exp: 1654087760, nbf: 1653482960, jti: "227ca1ff-7da5-430e-91c3-9e4107c5d4a8", sub: 1, }), "base64" ).toString("utf8");Ï // 构造 hmac const hmac = crypto.createHmac("sha256", secret); // 设置加密文本, 采用 header.payload 的连接方式 hmac.update(header + "." + payload); // 进行签名后，通过 hex 获取 base64 摘要。 let sig = hmac.digest("base64"); // base的结果中可能尾随一个或多个= 补位，需要去除 sig = sig.replace(/=+$/ig,''); console.log(sig); // 输出： 6LXlvsswMsKJpHD3ChsSGOzEJnYwm6eF212JE7Uc4p4 与前文中的token第三段 6LXlvsswMsKJpHD3ChsSGOzEJnYwm6eF212JE7Uc4p4 一致。当 header+payload一致时且秘钥相同，则生成的签名一致。这个也是JWT 校验采用的方式
总览前端发展史前言浏览器的发展史走进前端HTMLCSSjavaScript小前端时代大前端时代写在最后前言目前在IT公司中前端的岗位越来越成为不可或缺的，前端的地位也愈见明显，很多学校已经体系的传授前端课程，众多培训机构也将前端知识作为了主流课程，也有越来越多的同学加入到前端学习的行列中，作为前端工程师或者前端的学习者我们有必要去了解前端的发展史。那么首先让我们来了解一下浏览器的发展历程。浏览器的发展史在中享思途的行业资讯中看到了他对浏览器的发展史总结如下： 1、1991年，WorldWideWeb浏览器发布，这款可以说是祖父级别的，它是全球用户最早注册的WEB浏览器，WorldWideWeb开发始于20世纪80年代末，之后10年间经过MTV的宣传以及Commodore 64的完善，终于在1991年这个有跨时代意义的Web浏览器发布了。WorldWideWeb原理很简单，可以显示基本的样式表。2、1992年，Erwise发布，他被称为世界上第一个图形网页浏览器，它是由赫尔辛基科技大学的4个芬兰学生开发，主要是针对在Unix上运行 XWindows的需要。ViolaWWW，它是最高老的浏览器之一，在1992年5月份发布，它的开发者是美国加州大学的学生Pei-Yuan Wei，他的目的和Erwise一样是针对在Unix上运行 X Windows的需要。MidasWWW也是1992年发表的，它是由托尼约翰逊在斯坦福线型加速器中心研发的。Lynx首发也是在1992年，它提供了一个语音转换接口为视觉和听觉障碍的人群提供了很好的功能。3、1993年，Mosaic1.0发布，Mosaic可以说在一定程度上升华了Erwise的图形化，并且将图形化浏览器推向了万维网的主流。Arena浏览器是第一个支持背景图片，表格、文字绕流图片和内嵌数学表达式的浏览器，支持背景图片，表格等。4、1994年，Cello发布，他支持的就比较多了，WorldWideWeb、Gopher、FTP、CSO/ph/qi、Usenet News retrieval以及其他的一些协议，他当时成为了Windows平台上第一款浏览器。IBM WebExplorer也在同年发布，IBM推出自己的操作系统，WebExplorer也是强行捆绑在了他的系统系统上。Netscape Navigator它是一款独立的浏览器，而其在当时他的市场份额远远超过了微软的Internet Explorer。5、1995年，IE发布，浏览器之战也就此爆发。微软的Internet Explorer在此年首次推出，为了打造最好的浏览器，微软的源代码从Mosaic上获益匪浅，学习并迅速开始调整自己的浏览器，IE早期并没有捆绑在Window95上，而是作为Window95插件包的一部分进行捆绑，当时他只有1M大小。IE2在同年晚些时间发布，微软当时声称这是一个跨平台的浏览器，支持Window和Mac，IE2也加入了支持JavaScript的潮流中，它支持安全套接字层（SSL），Cookie和包括英语在内的12种语言。IE3正式捆绑在了Windows 95OSR2中，它是第一个支持CSS的主流浏览器，IE3摆脱了Spyglass源代码的局限，开始支持ActiveX控件，Java小程序，和Internet邮件。6、1996年，Opera发布，Telenor是挪威最大的通讯公司，他们推出了Opera，并在两年后进军移动市场，推出了Opera的移动版。7、1997年，IE4伴随着Windows 98操作系统一起发布。在发布Window Xp之前发布了IE6，并且新系统中包括了浏览器的最新升级补丁。随后IE逐渐占据了市场绝大部分份额。其他浏览器败下阵来。8、1998年，Netscape浏览器走向开源，和IE之争最后以失败告终。9、2003年，苹果Safari浏览器发布，苹果进入浏览器市场推出了自己的Webkit引擎，该引擎非常优秀，后来被Google，Nokia之类的厂商用于手机浏览器。10、2004年，Firefox引发了第二场浏览器之争。11、2006年，IE7发布，IE6发布的第六年，迫于Firefox的压力，微软匆匆推出IE7应战，吸取了Firefox的一些设计思想。12、2008年，Google的Chrome参战，Google发布了他们自己的浏览器，以轻量级，快，异常的稳固让这款浏览器成为了一个不可轻视的对手。 以上就是浏览器的一个大致发展历程了。接下来看看HTML的一个发展史。走进前端首先前端是什么呢？这个好像大家没有对他一个最完美的诠释，记得在上家公司，有一天与我们公司老板做了同一趟电梯，老板问道你是做什么的，我回答在咱们公司做前端开发，能不能跟我说一下什么是前端开发？老板随和的问道，毫无准备的我脑海中飘出很多词汇！你能看到的所有东西都是我们前端来做的。。。后端为我们提供逻辑、数据我们来给用户做展示，老板说能不能用一个词来涵盖，嗯。。。。数据可视化。老板说好，我明白了。其实我也不知道这样表述是否准确，但是在我的理解中是没有问题的哈，94将数据展示出来嘛哈哈！看到一篇文章中他是这样解释的： 前端指的就是浏览器端，后端指的就是为浏览器提供服务和数据的服务器端。这是比较准确的描述，如果往大了讲，你所有看到的一切，网页、移动端网页、小程序、甚至某些APP，都是属于前端的范畴。下面博主就带领大家见证前端是如何发展到今天的！上个世纪90 年代初，第一个网站便诞生，当时的网页完完全全是由文本所构成，除了一些小图片就是毫无布局可言的标题与段落。但是发展到今天来看我们各种用户操作逻辑的变更，我们各种炫酷样式的迭代，以及各种用户行为的理想化其实这些界面的变革都是我们前端的进步！下面介绍的三位就是我们前端重中之重，最重要的三门技术，如果我们把前端比喻成一部手机，那么HTML可以说成是我们手机的硬件、CSS他就是我们手机的外观色彩、javaScript则是我们手机的功能！即使前端出现更多的高级框架，其实也都是在不同程度的去封装我们这三门基础语言。HTMLHTML（超文本标记语言——HyperText Markup Language）是构成 Web 世界的基石。HTML是一种基础技术，常与CSS、JavaScript一起被众多网站用于设计令人赏心悦目的网页、网页应用程序以及移动应用程序的用户界面。1、HTML 1.0 超文本标记语言(第一版) – 在1993年6月发为互联网工程工作小组(IETF)工作草案发布(并非标准)2、HTML 2.0 – 1995年11月作为RFC 1866发布，在RFC 2854于2000年6月发布之后被宣布已经过时3、HTML 3.2 – 1996年1月14日，W3C推荐标准4、HTML 4.0 – 1997年12月18日，W3C推荐标准5、HTML 4.0(微小改进) – 1999年12月24日，W3C推荐标准6、XHTML 1.0 – 发布于2000年1月26日，是W3C推荐标准，后来经过修订于2002年8月1日重新发布7、XHTML 1.1 – 于2001年5月31日发布8、HTML5.0 2014年10月28日，万维网联盟宣布，经过接近8年的艰苦努力，该标准规范终于制定完成CSS层叠样式表(英文全称：Cascading StyleSheets)是一种用来表现HTML（标准通用标记语言的一个应用）或XML（标准通用标记语言的一个子集）等文件样式的计算机语言。CSS不仅可以静态地修饰网页，还可以配合各种脚本语言动态地对网页各元素进行格式化。1994 年，Hkon Wium Lie 最初提出了 CSS 的想法，联合当时正在设计 Argo 的浏览器的Bert Bos，他们决定一起合作设计 CSS，于是创造了 CSS 的最初版本。紧接着，他们在芝加哥的Mosaic and the Web 大会上第一次正式提出了 CSS 的建议，1995 年他们一起再次展示了这个建议。当时 W3C 刚刚建立，W3C 对 CSS 很感兴趣，为此专门组织了一次讨论会。1996 年 12 月，W3C 推出了 CSS 规范的第一版本。1997 年，W3C 颁布 CSS1.0 版本 ，CSS1.0 较全面地规定了文档的显示样式，可分为选择器、样式属性、伪类 / 对象几个部分。这一规范立即引起了各方的关注，随即微软和网景公司的浏览器均能支持 CSS1.0，这为 CSS 的发展奠定了基础。1998 年，W3C 发布了 CSS 的第二个版本，目前的主流浏览器都采用这标准。CSS2 的规范是基于 CSS1 设计的，包含了 CSS1 所有的功能，并扩充和改进了很多更加强大的属性。包括选择器、位置模型、布局、表格样式、媒体类型、伪类、光标样式。2005 年 12 月，W3C 开始 CSS3 标准的制定javaScriptJavaScript作为赋予网页生命的前端基础技术，它可以实现相应的效果和交互，是前端开发不可或缺的基本配置之一。因此，今天小编就来带家了解一下JavaScript的前世今生，具体内容包括JavaScript 的诞生、语言版本的更迭以及应用领域等等。让大家在入门开始学习JavaScript之前，对JavaScript的发展历史有一个大致的了解。JavaScript诞生于1995年，当时，它的主要目的是处理以前由服务器端需要负责的一些没有输入验证操作。在JavaScript问世之前，必须把表单数据发送到服务器端才能确定用户是否没有填写某个必填域，是否输入了无效的值。Netscape Navigator希望通过JavaScript来解决这个问题。在人们普遍使用电话拨号上网的时代，能够在客户端完成一些基本的验证任务绝对是令人兴奋的。毕竟，拨号上网速度之慢，导致了与服务器的每一次数据交换事实上都成了对人们耐心的一次考验。由此诞生了JavaScript!年份版本更新内容1997ECMAScript1第一版本1998ECMAScript2版本变更1999ECMAScript3添加正则表达式 try/catch1997ECMAScript4没有正式发布2009ECMAScript5添加‘strict mode‘ 严格添加 JSON支持2011ECMAScript5.1版本变更2015ECMAScript6添加类和模块2016ECMAScript7添加指数运算符（**）Array.prototype.includes2018ECMAScript8添加rest/spread属性、异步迭代、promise.finally()、RegExp小前端时代其实在小前端时代之前还有更加惨淡的阶段，甚至都么有前端的概念哈，这里呢就不来给大家大篇幅的来说了，不是因为不想说而是因为博主不太专业哈，就是我们的PHP嵌套前端代码以及JSP搭建网站等等；1995年，这是个好年份，又是这个搞事的网景公司，拜托一位叫布兰登-艾奇的大佬，希望开发出一个类似 Java 的脚本语言，用来提升浏览器的展示效果，增强动态交互能力。结果大佬喝着啤酒抽着烟，十来天就把这个脚本语言写出来了，功能很强大，就是语法一点都不像Javao 这样就渐渐形成了前端的雏形：HTML为骨架，CSS 为外貌，JavaScript 为交互。同时期微软等一些公司也针对自家浏览器开发出了自己的脚本语言。浏览器五花八门，虽然有了比较统一的 ECMA 标准，但是浏览器先于标准在市场上流行开来，成为了事实标准。导致，现在前端工程师还要在做一些政府古老项目的时候，还要去处理浏览器兼容（万恶的IE系列）哈哈 不过现在距离IE还有半个月就退出舞台啦，前端人的噩梦结束了。随着时代的不断进步，接下来就出现了表格布局，随后是 Flash，最后是基于 css的网页设计。简单来说，前端页面重构技术经历着这样的一个过程：table布局页面DIV+CSS =》HTML5+CSS3， 并且面对众多的浏览器与平合，web 前端己不再是以前那样简单的重复性工作。不管怎么说，前端开发也算是能写点逻辑代码了，不再是只能画画页面的低端开发了。随着1998年 ajax 的出现，前端开发从 web1.0迈向了web2.0，甚至目前渐渐转向了web3.0；前端从纯内容的静态展示，发展到了动态网页，富交互，前端数据处理的新时期。这一时期，比较知名的两个富交互动态的浏览器产品是： • Gmail (2004年） • Google 地图 （2005年） 由于动态交互、数据异步 请求的需求增多，还衍生出了jQuery (2006） 这样优秀的跨浏览器的 js 工具库，主要用于 DOM操作，数据交互。有些古老的项目现在还在使用 jQuery。大前端时代前端开发的前身是 “网页制作”，随着移动端的兴起和4G、5G技术的普及，互联网产品业务越来越复杂，在 2011年前后，逐步脱变为前端开发；在2011之前，前端后端是不分家的，2011后智能手机普及了，场景也越来越丰富复杂了，不得不把前端独立出来，也是目前主流的前后端分离开发模式。2016年前后，前端开发突然迎来了技术井喷期，开发形式突然发生了翻天覆地的变化。nodejs 突然火了，2009年诞生，2015到2016左右在中国就火起来了，node火了之后带来了连锁反应2016年前后，以 webpack为代表的 Node.js 工作流工具使前端开发的开发形式产生了翻天覆地的变化，并且，随着 Vue / React 的诞生，使前端开发进入了框架时代，工程化时代；甚至具备了微前端的概念。写在最后其实今天跟大家聊的并不是很多，而是大概的来梳理了一下前端发展大的节点，等后面博主会出一期更加细致的前端发展的历程，利用语言出现的使命环环相扣！以及我们的开发模式的MVC、MVP、MVVM的变革我们放到后期来说；其实我们回首 web 前端开发的过去和现在，对于我们岗位的称呼也相应地不断发生着改变：页面小工、切图小工、美工、页面制作，再到现在的“前端开发”，由于 html 与css入门起点较低，小公司里的“美工们”也许至今还干着这样的工作：出图 （Photoshop/Firework)切图（HTML/CSS)，现在而言前端开发并非如此了，随着越来越多的前端框架的兴起，我们如今的前端PC、APP、H5、xiao cheng xu、公众号、百度、支付宝小程序等多端应用一名前端开发工程师足以，利用不同的技术框架更加专业的编写出不同应用端的代码，以及前端甚至可以利用node脱离后端，我们一些框架甚至直接可以基于云进行开发，在当前云原生火爆之时，云函数、云存储甚至不用任何后端语言皆可进行开发！总结一下就是：Web 前端开发其实是很牛的职位！很多前端伙伴问题有没有体系的面试题？ 今天为大家推荐一款刷题神奇哦 点击链接访问牛客网 各大互联网大厂面试真题。从基础到入阶乃至原理刨析类面试题 应有尽有，赶快来装备自己吧！助你面试稳操胜券，solo全场面试官本期图书鉴赏（1）零基础也能快速入门。本书从最基础的高等数学基础讲起，由浅入深，层层递进，在巩固固有知识的同时深入讲解人工智能的算法原理，无论读者是否从事计算机相关行业，是否接触过人工智能，都能通过本书实现快速入门。 （2）全新视角介绍数学知识。采用计算机程序模拟数学推论的介绍方法，使数学知识更为清晰易懂，更容易让初学者深入理解数学定理、公式的意义，从而激发起读者的学习兴趣。 （3）理论和实践相结合。每章最后提供根据所在章的理论知识点精心设计的“综合性实例”，读者可以通过综合案例进行实践操作，为以后的算法学习奠定基础。 （4）大量范例源码+习题答案，为学习排忧解难。本书所有示例都有清晰完整的源码，每章之后设有习题并配套题目答案，讲解清晰，解决读者在学习中的所有困惑。本书以零基础讲解为宗旨，面向学习数据科学与人工智能的读者，通俗地讲解每一个知识点，旨在帮助读者快速打下数学基础。全书分为 4 篇，共 17 章。其中第 1 篇为数学知识基础篇，主要讲述了高等数学基础、微积分、泰勒公式与拉格朗日乘子法；第 2 篇为数学知识核心篇，主要讲述了线性代数基础、特征值与矩阵分解、概率论基础、随机变量与概率估计；第 3 篇为数学知识提高篇，主要讲述了数据科学的几种分布、核函数变换、熵与激活函数；第 4 篇为数学知识应用篇，主要讲述了回归分析、假设检验、相关分析、方差分析、聚类分析、贝叶斯分析等内容。技术探讨/资料获取/职位推荐 微信名片
Treejs系列--7相机序言正交相机透视相机立体相机立方相机序言摄像机在3D演示中扮演着重要的角色，Treejs提供了Camera对象及其子对象，可以非常方便的应用。正交相机使用正交投影来进行投影，无论物体距离相机距离远或者近，在最终渲染的图片中物体的大小都保持不变。渲染2D场景或者UI元素非常有用。OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number ) left: 摄像机视锥体左侧面。 right: 摄像机视锥体右侧面。 top: 摄像机视锥体上侧面。 bottom: 摄像机视锥体下侧面。 near: 摄像机视锥体近端面。 far: 摄像机视锥体远端面。 改变物体距离相机距离，物体大小始终如下 var camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 0.1, 1000 ); camera.position.z = 1; //改变相机距离物体位置，物体保持不变 //camera.position.z = 999; 透视相机这一摄像机使用透视投影来进行投影，用来模拟人眼所看到的景象，它是3D场景的渲染中使用最普遍的投影模式。PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number ) fov: 摄像机视锥体垂直视野角度 aspect: 摄像机视锥体长宽比 near: 摄像机视锥体近端面 far: 摄像机视锥体远端面 改变相机距离物理距离为466 改变相机距离物理距离为66 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); //改变相机距离物体位置，犹如人眼平常看到的景象，远小近大。 camera.position.z = 466; //camera.position.z = 66; 立体相机双透视摄像机常被用于创建3D立体影像或者视差效果。StereoCamera(aspect : Float,eyeSep : Float,cameraL : PerspectiveCamera,cameraR : PerspectiveCamera) aspect: 摄像机视锥体的长宽比，默认值是1。例如设置为场景的宽高比。 eyeSep: 模拟人的瞳孔间距，默认值是0.064 cameraL: 左摄像机 cameraR: 右摄像机 立方相机创建6台相机，将它们的拍摄结果渲染。//构造器 CubeCamera( near : Number, far : Number, cubeResolution : Number ) near: 近剪切面的距离 far: 远剪切面的距离 cubeResolution: 设置立方体边缘的长度 //创建6台相机 var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 ); scene.add( cubeCamera ); //材质中设置环境贴图为相机拍摄 var chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeCamera.renderTarget.texture } ); var car = new Mesh( carGeometry, chromeMaterial ); scene.add( car ); // 更新相机 car.setVisible( false ); cubeCamera.position.copy( car.position ); cubeCamera.update( renderer, scene ); //渲染场景 car.setVisible( true ); renderer.render( scene, camera );
【Echarts数据可视化】一个项目带你学会酷炫的数据可视化
文章の目录1.学会使用echarts库里的各种矢量图形表 2. 学会通过修改库中矢量图形的参数去定制自己需要的矢量图表 3. 学会边框图形的用法 应对现在数据可视化的趋势，越来越多企业需要在很多场景(营销数据，生产数据，用户数据)下使用，可视化图表来展示体现数据，让数据更加直观，数据特点更加突出。我们引入 ‘立可得’ 数据可视化项目。 ​ 该项目除了使用了基础的DIV+CSS布局，还引入了一些C3技术，还引入了各类图表的绘制，以及高级的地图数据可视化案例。主要功能有：饼状图、柱状图、线形图、地图 …最终效果展示： 完成该项目需要具备以下知识：div + css 布局flex 布局css3动画css3渐变css3边框图片原生js + jquery 使用rem适配echarts基础粗略代码统计：css 580行html 450行js 400行 (有效) ECharts，一个使用 JavaScript 实现的开源可视化库，可以流畅的运行在 PC 和移动设备上，兼容当前绝大部分浏览器（IE8/9/10/11，Chrome，Firefox，Safari等），底层依赖矢量图形库 ZRender，提供直观，交互丰富，可高度个性化定制的数据可视化图表。通俗讲：是一个JS插件性能好可流畅运行PC与移动设备兼容主流浏览器提供很多常用图表，且可定制。折线图、柱状图、散点图、饼图、K线图官方教程链接下载echarts引入echarts dist/echarts.min.js准备一个具备大小的DOM容器代码示例： 初始化echarts实例对象var myChart = echarts.init(document.getElementById('main')); 指定配置项和数据(option)var option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line' }] }; 将配置项设置给echarts实例对象myChart.setOption(option); 以下配置很重要，小伙伴们一定要牢记哦！：series xAxis yAxis grid tooltip title legend colorseries系列列表。每个系列通过 type 决定自己的图表类型：图标数据，指定什么类型的图标，可以多个图表重叠。xAxis：直角坐标系 grid 中的 x 轴boundaryGap: 坐标轴两边留白策略 true，这时候刻度只是作为分隔线，标签和数据点都会在两个刻度之间的带(band)中间。yAxis：直角坐标系 grid 中的 y 轴grid：直角坐标系内绘图网格。title：标题组件tooltip：提示框组件legend：图例组件color：调色盘颜色列表数据堆叠，同个类目轴上系列配置相同的stack值后 后一个系列的值会在前一个系列的值上相加。代码实例：var option = { color: ['pink', 'blue', 'green', 'skyblue', 'red'], title: { text: '我的折线图' }, tooltip: { trigger: 'axis' }, legend: { data: ['直播营销', '联盟广告', '视频广告', '直接访问'] }, grid: { left: '3%', right: '3%', bottom: '3%', // 当刻度标签溢出的时候，grid 区域是否包含坐标轴的刻度标签。如果为true，则显示刻度标签 // 如果left right等设置为 0% 刻度标签就溢出了，此时决定是否显示刻度标签 containLabel: true }, toolbox: { feature: { saveAsImage: {} } }, xAxis: { type: 'category', // 坐标轴两边留白策略 true，这时候刻度只是作为分隔线，标签和数据点都会在两个刻度之间的带(band)中间。 boundaryGap: false, data: ['星期一', '星期二', '周三', '周四', '周五', '周六', '周日'] }, yAxis: { type: 'value' }, series: [ { name: '直播营销', // 图表类型是线形图 type: 'line', data: [120, 132, 101, 134, 90, 230, 210] }, { name: '联盟广告', type: 'line', data: [220, 182, 191, 234, 290, 330, 310] }, { name: '视频广告', type: 'line', data: [150, 232, 201, 154, 190, 330, 410] }, { name: '直接访问', type: 'line', data: [320, 332, 301, 334, 390, 330, 320] } ] }; 设计稿是1920pxPC端适配： 宽度在 1024~1920之间页面元素宽高自适应flexible.js 把屏幕分为 24 等份cssrem 插件的基准值是 80px 插件-配置按钮—配置扩展设置–Root Font Size 里面 设置。 `但是别忘记重启vscode软件保证生效` 要把屏幕宽度约束在1024~1920之间有适配。// 实现rem适配@media screen and (max-width: 1024px) { html { font-size: 42.66px !important; }}@media screen and (min-width: 1920px) { html { font-size: 80px !important; }}效果图： 1920px * 1078pxbody 设置背景图 ，行高1.15viewport 主体容器，限制最小宽度1024px，与最大宽度1920px，最小高度780px。需要居中显示使用logo.png做为背景图，在容器内显示内间距 88px 20px 0column 列容器，分三列，占比 3：4：3中间容器外间距 32px 20px 0 /* 基础布局 */ body{ font-family: Arial, Helvetica, sans-serif; margin: 0; padding: 0; font-size: 0.5rem; line-height: 1.15; background: url(../images/bg.jpg) no-repeat 0 0 / cover; } h4,h3,ul{ margin: 0; padding: 0; font-weight: normal; } ul{ list-style: none; } a{ text-decoration: none; } .viewport{ max-width: 1920px; min-width: 1024px; margin: 0 auto; min-height: 780px; padding: 3.667rem 0.833rem 0; background: url(../images/logo.png) no-repeat 0 0 / contain; display: flex; } .column{ flex: 3; position: relative; } .column:nth-child(2){ flex: 4; margin: 1.333rem 0.833rem 0; } css3中自适应边框图片运用：组合写法：border-image: url("images/border.jpg") 167/20px round; 拆分写法：border-image-source: url("images/border.jpg"); border-image-slice: 167 167 167 167; border-image-width: 20px; border-image-repeat: round; 解释：第一行是边框图片资源地址第二行是裁剪尺寸（上 右 下 左）单位默认px，可使用百分百。第三行是边框图片的宽度，默认边框的宽度。第四行是平铺方式：stretch 拉伸（默认）repeat 平铺，从边框的中心向两侧开始平铺，会出现不完整的图片。round 环绕，是完整的使用切割后的图片进行平铺。代码实例： 边框图片 因为所有的面板的基础样式是一致的，提前写好一个公用的面板类。 面板 .panel容器 .inner 内边距是 上下24px 左右 36px代码示例：/* 公共面板样式 */ .panel { position: relative; border: 15px solid transparent; border-width: .6375rem .475rem .25rem 1.65rem; border-image-source: url(../images/border.png); border-image-slice: 51 38 20 132; margin-bottom: .25rem; } .inner { position: absolute; top: -0.6375rem; left: -1.65rem; right: -0.475rem; bottom: -0.25rem; padding: .3rem .45rem; } .panel h3 { font-size: 0.25rem; color: #fff; font-weight: 400; } 代码示例： 2,190 设备总数 190 季度新增 3,001 运营设备 108 异常设备 样式描述：容器高度 110pxh4字体 28px #fff 左边距 4.8px 下间隙 8pxspan字体 16px #4c9bfd代码示例：/* 概览区域 */ .overview { height: 1.375rem; } .overview ul { display: flex; justify-content: space-between; } .overview ul li h4 { font-size: .35rem; color: #fff; margin: 0 0 .1rem .06rem; } .overview ul li span { font-size: .2rem; color: #4c9bfd; } 代码示例： 故障设备监控 异常设备监控 故障时间 设备地址 异常代码 20180701 11北京市昌平西路金燕龙写字楼 1000001 20190601 北京市昌平区城西路金燕龙写字楼 1000002 20190704 北京市昌平区建材城西路金燕龙写字楼 1000003 20180701 北京市昌平区建路金燕龙写字楼 1000004 20190701 北京市昌平区建材城西路金燕龙写字楼 1000005 20190701 北京市昌平区建材城西路金燕龙写字楼 1000006 20190701 北京市昌平区建西路金燕龙写字楼 1000007 20190701 北京市昌平区建材城西路金燕龙写字楼 1000008 20190701 北京市昌平区建材城西路金燕龙写字楼 1000009 20190710 北京市昌平区建材城西路金燕龙写字楼 1000010 异常时间 设备地址 异常代码 20190701 北京市昌平区建材城西路金燕龙写字楼 1000001 20190701 北京市昌平区建材城西路金燕龙写字楼 1000002 20190703 北京市昌平区建材城西路金燕龙写字楼 1000002 20190704 北京市昌平区建材城西路金燕龙写字楼 1000002 20190705 北京市昌平区建材城西路金燕龙写字楼 1000002 20190706 北京市昌平区建材城西路金燕龙写字楼 1000002 20190707 北京市昌平区建材城西路金燕龙写字楼 1000002 20190708 北京市昌平区建材城西路金燕龙写字楼 1000002 20190709 北京市昌平区建材城西路金燕龙写字楼 1000002 20190710 北京市昌平区建材城西路金燕龙写字楼 1000002 监控区域 monitor 大盒子的高度是 480px.tabs 标签选项 加上active激活选项 默认激活第一个选项.content 切换内容 加上style="display: block;"显示内容 默认激活第一个内容代码示例：/* 监控区域 */ .monitor{ height: 6rem; } .monitor .inner{ padding: .3rem 0; display: flex; flex-direction: column; } .monitor .tabs{ padding: 0 .45rem; margin-bottom: 0.225rem; display: flex; } .monitor .tabs a{ color:#1950c4; font-size: 0.225rem; padding: 0 .3375rem; } .monitor .tabs a:first-child{ padding-left: 0; border-right: 0.025rem solid #00f2f1; } .monitor .tabs a.active{ color: #fff; } .monitor .content{ flex: 1; position: relative; display: none; } .monitor .head{ display: flex; justify-content: space-between; line-height: 1.05; background-color: rgba(255, 255, 255, 0.1); padding: 0.15rem 0.45rem; color: #68d8fe; font-size: 0.175rem; } .monitor .marquee-view { position: absolute; top: 0.5rem; bottom: 0; width: 100%; overflow: hidden; } .monitor .row{ display: flex; justify-content: space-between; line-height: 1.05; font-size: 0.15rem; color: #61a8ff; padding: 0.15rem 0.45rem; } .monitor .row .icon-dot{ position: absolute; left: 0.2rem; opacity: 0; } .monitor .row:hover { background-color: rgba(255, 255, 255, 0.1); color: #68d8fe; } .monitor .row:hover .icon-dot{ opacity: 1; } .monitor .col:first-child{ width: 1rem; } .monitor .col:nth-child(2){ width: 2.5rem; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } .monitor .col:nth-child(3){ width: 1rem; } } .inner 容器内间距 24px 0.tabs 容器内间距 0 36pxa 容器 颜色： #1950c4 内间距：0 27px 字体：18px第一个a容器 去除左侧内间距 加上右侧2px宽度边框#00f2f1激活的时候 颜色白色.content容器占满剩余高度 flex:1默认隐藏.head 容器行高 1.05 背景 rgba(255, 255, 255, 0.1) 内间距 12px 36px 颜色 #68d8fe 字体大小 14pxrow 容器行高 1.05 内间距 12px 36px 颜色 #68d8ff 字体大小 12px.icon-dot 字体图标 绝对定位 左边0.2rem 透明度0鼠标经过后：背景 rgba(255, 255, 255, 0.1) 透明度1col容器宽度：1rem 2.5rem 1rem第二个col 一行不换行 溢出 省略1、绑定 标签页点击 事件 2、当前容器加active其他容器移除active 3、index对应的内容容器显示其他容器隐藏代码示例： // 切换 $(".monitor .tabs").on("click", "a", function() { $(this).addClass("active").siblings("a").removeClass("active"); // console.log($(this).index()); // 选取对应索引号的content $(".monitor .content").eq($(this).index()).show().siblings(".content").hide(); }); 1、先克隆列表，追加在后面 2、marquee-view 占满剩余高度，溢出隐藏 3、绝对定位，top 1.6rem bottom 0 4、宽度100%，溢出隐藏 5、使用animation实现动画 6、使用 translateY 向上位移 50% 7、动画时间15s，匀速播放，循环执行。代码示例： // 动画 $(".marquee-view .marquee").each(function() { // console.log($(this)); var rows = $(this).children().clone(); $(this).append(rows); }); 代码示例：/* 通过CSS3动画滚动marquee */ .marquee-view .marquee { animation: move 15s linear infinite; } @keyframes move { 0% { } 100% { transform: translateY(-50%); } } /* 3.鼠标经过marquee 就停止动画 */ .marquee-view .marquee:hover { animation-play-state: paused; } 代码示例： 点位分布统计 320,11 点位总数 418 本月新增 point 盒子高度为 340px代码示例：/* 点位 */ .point { height: 4.25rem; } .point .chart { display: flex; margin-top: 0.3rem; justify-content: space-between; } .point .pie { width: 3.9rem; height: 3rem; margin-left: -0.125rem; background-color: pink; } .point .data { display: flex; flex-direction: column; justify-content: space-between; width: 2.1rem; padding: .45rem .375rem; box-sizing: border-box; background-image: url(../images/rect.png); background-size: cover; } .point h4 { margin-bottom: 0.15rem; font-size: .35rem; color: #fff; } .point span { display: block; color: #4c9bfd; font-size: .2rem; } ECharts图表实现步骤：从官方示例中找到类似图表，适当修改，引入到HTML页面中。按照产品需求，来定制图表。代码示例：// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} {b} : {c} ({d}%)" }, series: [ { name: "面积模式", type: "pie", radius: [30, 110], center: ["75%", "50%"], roseType: "area", data: [ { value: 10, name: "rose1" }, { value: 5, name: "rose2" }, { value: 15, name: "rose3" }, { value: 25, name: "rose4" }, { value: 20, name: "rose5" }, { value: 35, name: "rose6" }, { value: 30, name: "rose7" }, { value: 40, name: "rose8" } ] } ] }; // 3. 配置项和数据给我们的实例化对象 myChart.setOption(option); })(); 请小伙伴们先参考官方例子，熟悉里面参数具体含义代码示例：option = { // 提示框组件 tooltip: { // trigger 触发方式。 非轴图形，使用item的意思是放到数据对应图形上触发提示 trigger: 'item', // 格式化提示内容： // a 代表series系列图表名称 // b 代表series数据名称 data 里面的name // c 代表series数据值 data 里面的value // d代表 当前数据/总数据的比例 formatter: "{a} {b} : {c} ({d}%)" }, // 控制图表 series: [ { // 图表名称 name: '点位统计', // 图表类型 type: 'pie', // 南丁格尔玫瑰图 有两个圆 内圆半径10% 外圆半径70% // 饼形图半径。 可以是像素。也可以是百分比（ 基于DOM容器大小）第一项是内半径，第二项是外半径（通过它可以实现饼形图大小） radius: ['10%', '70%'], // 图表中心位置 left 50% top 50% 距离图表DOM容器 center: ['50%', '50%'], // radius 半径模式，另外一种是 area 面积模式 roseType: 'radius', // 数据集 value 数据的值 name 数据的名称 data: [ {value:10, name:'rose1'}, {value:5, name:'rose2'}, {value:15, name:'rose3'}, {value:25, name:'rose4'}, {value:20, name:'rose5'}, {value:35, name:'rose6'}, {value:30, name:'rose7'}, {value:40, name:'rose8'} ] } ] }; 需求1：颜色设置color: ['#006cff', '#60cda0', '#ed8884', '#ff9f7f', '#0096ff', '#9fe6b8', '#32c5e9', '#1d9dff'],需求2：修改饼形图大小 ( series对象)radius: ['10%', '70%'],需求3： 把饼形图的显示模式改为 半径模式roseType: "radius",需求4：数据使用更换（series对象 里面 data对象） { value: 20, name: '云南' }, { value: 26, name: '北京' }, { value: 24, name: '山东' }, { value: 25, name: '河北' }, { value: 20, name: '江苏' }, { value: 25, name: '浙江' }, { value: 30, name: '四川' }, { value: 42, name: '湖北' } 需求5：字体略小些 10 px ( series对象里面设置 )饼图图形上的文本标签可以控制饼形图的文字的一些样式。 label 对象设置series: [ { name: "面积模式", type: "pie", radius: [30, 110], center: ["50%", "50%"], roseType: "radius", // 文本标签控制饼形图文字的相关样式， 注意它是一个对象 label: { fontSize: 10 }, } ]};需求6：防止缩放的时候，引导线过长。引导线略短些 (series对象里面的 labelLine 对象设置 )连接图表 6 px连接文字 8 px // 文字调整label:{fontSize: 10},// 引导线调整labelLine: {// 连接扇形图线长length: 6,// 连接文字线长length2: 8}}],需求6：浏览器缩放的时候，图表跟着自动适配。// 监听浏览器缩放，图表对象调用缩放resize函数window.addEventListener("resize", function() {myChart.resize();});代码示例：// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} {b} : {c} ({d}%)" }, // 注意颜色写的位置 color: [ "#006cff", "#60cda0", "#ed8884", "#ff9f7f", "#0096ff", "#9fe6b8", "#32c5e9", "#1d9dff" ], series: [ { name: "点位统计", type: "pie", // 如果radius是百分比则必须加引号 radius: ["10%", "70%"], center: ["50%", "50%"], roseType: "radius", data: [ { value: 20, name: "云南" }, { value: 26, name: "北京" }, { value: 24, name: "山东" }, { value: 25, name: "河北" }, { value: 20, name: "江苏" }, { value: 25, name: "浙江" }, { value: 30, name: "四川" }, { value: 42, name: "湖北" } ], // 修饰饼形图文字相关的样式 label对象 label: { fontSize: 10 }, // 修饰引导线样式 labelLine: { // 连接到图形的线长度 length: 6, // 连接到文字的线长度 length2: 8 } } ] }; // 3. 配置项和数据给我们的实例化对象 myChart.setOption(option); // 4. 当我们浏览器缩放的时候，图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); })(); 代码示例： 设备数据统计 代码示例：/* 地图 */ .map { height: 7.225rem; margin-bottom: 0.25rem; display: flex; flex-direction: column; } .map h3 { line-height: 1; padding: 0.2rem 0; margin: 0; font-size: 0.25rem; color: #fff; font-weight: 400; } .map .icon-cube { color: #68d8fe; } .map .chart { flex: 1; background-color: rgba(255, 255, 255, 0.05); } .map .geo { width: 100%; height: 100%; } 注意第二列（column) 有个外边距（上面 32px 左右 20px 下是 0）.viewport .column:nth-child(2) { flex: 4; margin: .4rem .25rem 0; } 全国用户总量统计 120,899 用户总量 248 本月新增 /* 用户模块 */ .users { height: 4.25rem; display: flex; } .users .chart { display: flex; margin-top: .3rem; } .users .bar { width: 7.35rem; height: 3rem; } .users .data { display: flex; flex-direction: column; justify-content: space-between; width: 2.1rem; padding: .45rem .375rem; box-sizing: border-box; background-image: url(../images/rect.png); background-size: cover; } .users h4 { margin-bottom: .15rem; font-size: .35rem; color: #fff; } .users span { display: block; color: #4c9bfd; font-size: 0.2rem; } 从官方示例中找到最接近项目需求的例子，适当修改, 引入到HTML页面中按照产品需求，来定制图表。(function () { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".bar")); // 2. 指定配置和数据 var option = { // 工具提示 tooltip: { // 触发类型 经过轴触发axis 经过轴触发item trigger: 'axis', // 轴触发提示才有效 axisPointer: { // 默认为直线，可选为：'line' 线效果 | 'shadow' 阴影效果 type: 'shadow' } }, // 图表边界控制 grid: { // 距离 上右下左 的距离 left: '3%', right: '4%', bottom: '3%', // 是否包含文本 containLabel: true }, // 控制x轴 xAxis: [ { // 使用类目，必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // true意思：图形在刻度中间 // false意思：图形在刻度之间 alignWithLabel: true } } ], // 控制y轴 yAxis: [ { // 使用数据的值设为刻度文字 type: 'value' } ], // 控制x轴 series: [ { // 图表数据名称 name: '用户统计', // 图表类型 type: 'bar', // 柱子宽度 barWidth: '60%', // 数据 data: [10, 52, 200, 334, 390, 330, 220] } ] }; // 3. 把配置给实例对象 myChart.setOption(option); })(); 需求1： 修改柱子的颜色// 修改线性渐变色方式 1color: new echarts.graphic.LinearGradient( // (x1,y2) 点到点 (x2,y2) 之间进行渐变 0, 0, 0, 1, [ { offset: 0, color: '#00fffb' }, // 0 起始颜色 { offset: 1, color: '#0061ce' } // 1 结束颜色 ]),// 修改线性渐变色方式 2color: {type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'red' // 0% 处的颜色 }, { offset: 1, color: 'blue' // 100% 处的颜色 }], globalCoord: false // 缺省为 false},需求2： 提示框组件放到柱子上触发， 没有阴影等效果//提示框组件tooltip: {trigger: 'item',// axisPointer: { // 坐标轴指示器，坐标轴触发有效 这个模块我们此时不需要删掉即可 // type: 'shadow' // 默认为直线，可选为：'line' | 'shadow'// }},需求3： 修改柱形图表大小， 以及相关网格。饼形图修改图表大小是通过 series 对象里面的 radius柱形图修改图标大小是通过 series 对象里面的 grid 对象 left right 等显示网格 show: true，网格颜色是 borderColor// 直角坐标系内绘图网格（区域）grid: { top: '3%', right: '3%', bottom: '3%', left: '0%', // 图表位置紧贴画布边缘是否显示刻度以及label文字 防止坐标轴标签溢出跟grid 区域有关系 containLabel: true, // 是否显示直角坐标系网格 show: true, //grid 四条边框的颜色 borderColor: 'rgba(0, 240, 255, 0.3)'},需求4： X 轴调整柱子在刻度之间剔除刻度不显示刻度标签文字颜色 #4c9bfd 通过 axisLabel 对象设置修改x轴线的颜色 axisLine 里面的 lineStyle// 控制x轴 xAxis: [ { // 使用类目，必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // true意思：图形和刻度居中中间 // false意思：图形在刻度之间 alignWithLabel: false, // 不显示刻度 show: false }, // x坐标轴文字标签样式设置 axisLabel: { color: '#4c9bfd' }, // x坐标轴颜色设置 axisLine:{ lineStyle:{ color:'rgba(0, 240, 255, 0.3)', // width:8, x轴线的粗细 // opcity: 0, 如果不想显示x轴线 则改为 0 } } } 需求5： Y 轴调整剔除刻度不显示Y轴文字颜色 #4c9bfd 通过 axisLabel 对象设置Y轴分割线颜色 splitLine 对象里面 lineStyle 对象设置// 控制y轴 yAxis: [ { // 使用类目，必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // 不显示刻度 show: false }, // y坐标轴文字标签样式设置 axisLabel: { color: '#4c9bfd' }, // y坐标轴颜色设置 axisLine:{ lineStyle:{ color:'rgba(0, 240, 255, 0.3)', // width:8, x轴线的粗细 // opcity: 0, 如果不想显示x轴线 则改为 0 } }, // y轴 分割线的样式 splitLine: { lineStyle: { color: 'rgba(0, 240, 255, 0.3)' } } ], 需求6：调整数据，与省略图形定制// seriesdata: [2100,1900,1700,1560,1400,1200,1200,1200,900,750,600,480,240]// xAxisdata: ['上海', '广州', '北京', '深圳', '合肥', '', '......', '', '杭州', '厦门', '济南', '成都', '重庆']省略图形经过图形才显示提示，且省略的柱子不需要提示图形单独设置颜色// 中间省略的数据 准备三项 var item = { name:'', value: 1200, // 柱子颜色 itemStyle: { color: '#254065' }, // 鼠标经过柱子颜色 emphasis: { itemStyle: { color: '#254065' } }, // 工具提示隐藏 tooltip: { extraCssText: 'opacity:0' }, } // series配置data选项在中使用 data: [2100,1900,1700,1560,1400,item,item,item,900,750,600,480,240],// 4. 当我们浏览器缩放的时候，图表也等比例缩放 window.addEventListener("resize", function() {// 让我们的图表调用 resize这个方法 myChart.resize();});代码示例： 365天 90天 30天 24小时 20,301,987 订单量 99834 销售额(万元) 代码示例：/* 订单 */ .order { height: 1.875rem; } .order .filter { display: flex; } .order .filter a { display: block; height: 0.225rem; line-height: 1; padding: 0 0.225rem; color: #1950c4; font-size: 0.225rem; border-right: 0.025rem solid #00f2f1; } .order .filter a:first-child { padding-left: 0; } .order .filter a:last-child { border-right: none; } .order .filter a.active { color: #fff; font-size: 0.25rem; } .order .data { display: flex; margin-top: 0.25rem; } .order .item { width: 50%; } .order h4 { font-size: 0.35rem; color: #fff; margin-bottom: 0.125rem; } .order span { display: block; color: #4c9bfd; font-size: 0.2rem; } 代码示例：// 订单功能 (function(){ // 1. 准备数据 var data = { day365: { orders: '20,301,987', amount: '99834' }, day90: { orders: '301,987', amount: '9834' }, day30: { orders: '1,987', amount: '3834' }, day1: { orders: '987', amount: '834' } } // 获取显示 订单数量 容器 var $h4Orders = $('.order h4:eq(0)') // 获取显示 金额数量 容器 var $h4Amount = $('.order h4:eq(1)') $('.order').on('click','.filter a',function(){ // 2. 点击切换激活样式 $(this).addClass('active').siblings().removeClass('active') // 3. 点击切换数据 var currdata = data[this.dataset.key] $h4Orders.html(currdata.orders) $h4Amount.html(currdata.amount) }) // 4. 开启定时器切换数据 var index = 0 var $allTab = $('.order .filter a') setInterval(function(){ index ++ if (index >= 4) index = 0 $allTab.eq(index).click() },5000) })(); 代码示例： 销售额统计 年 季 月 周 单位:万 代码示例：/* 销售区域 */ .sales { height: 3.1rem; } .sales .caption { display: flex; line-height: 1; } .sales h3 { height: 0.225rem; padding-right: 0.225rem; border-right: 0.025rem solid #00f2f1; } .sales a { padding: 0.05rem; font-size: 0.2rem; margin: -0.0375rem 0 0 0.2625rem; border-radius: 0.0375rem; color: #0bace6; } .sales a.active { background-color: #4c9bfd; color: #fff; } .sales .inner { display: flex; flex-direction: column; } .sales .chart { flex: 1; padding-top: 0.1875rem; position: relative; } .sales .label { position: absolute; left: 0.525rem; top: 0.225rem; color: #4996f5; font-size: 0.175rem; } .sales .line { width: 100%; height: 100%; } 实现步骤：寻找官方的类似示例，给予分析, 引入到HTML页面中按照需求来定制它。代码示例： 官方参考示例// 销售统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".line")); // 2. 指定配置和数据 var option = { tooltip: { trigger: "axis" }, legend: { data: ["邮件营销", "联盟广告"] }, grid: { left: "3%", right: "4%", bottom: "3%", containLabel: true }, xAxis: { type: "category", boundaryGap: false, data: ["周一", "周二"] }, yAxis: { type: "value" }, series: [ { name: "邮件营销", type: "line", stack: "总量", data: [120, 132, 101, 134, 90, 230, 210] }, { name: "联盟广告", type: "line", stack: "总量", data: [220, 182, 191, 234, 290, 330, 310] } ] }; // 3. 把配置和数据给实例对象 myChart.setOption(option); })(); 根据需求定制图表，需求如下：需求1： 修改折线图大小，显示边框设置颜色：#012f4a，并且显示刻度标签。// 设置网格样式 grid: { top: '20%', left: '3%', right: '4%', bottom: '3%', show: true,// 显示边框 borderColor: '#012f4a',// 边框颜色 containLabel: true // 包含刻度文字在内 }, 需求2： 修改图例组件中的文字颜色 #4c9bfd， 距离右侧 right 为 10%// 图例组件legend: { textStyle: { color: '#4c9bfd' // 图例文字颜色 }, right: '10%' // 距离右边10% }, 需求3： x轴相关配置刻度去除x轴刻度标签字体颜色：#4c9bfd剔除坐标轴线颜色（将来使用Y轴分割线)轴两端是不需要内间距 boundaryGapxAxis: { type: 'category', data: ["周一", "周二"], axisTick: { show: false // 去除刻度线 }, axisLabel: { color: '#4c9bfd' // 文本颜色 }, axisLine: { show: false // 去除轴线 }, boundaryGap: false // 去除轴内间距 }, 需求4： y轴的定制刻度去除字体颜色：#4c9bfd分割线颜色：#012f4ayAxis: { type: 'value', axisTick: { show: false // 去除刻度 }, axisLabel: { color: '#4c9bfd' // 文字颜色 }, splitLine: { lineStyle: { color: '#012f4a' // 分割线颜色 } } }, 需求5： 两条线形图定制颜色分别：#00f2f1 #ed3f35把折线修饰为圆滑 series 数据中添加 smooth 为 truecolor: ['#00f2f1', '#ed3f35'], series: [{ name:'预期销售额', data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', // 折线修饰为圆滑 smooth: true, },{ name:'实际销售额', data: [100, 331, 200, 123, 233, 543, 400], type: 'line', smooth: true, }] 需求6： 配置数据// x轴的文字xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],// 图标数据series: [{ name:'预期销售额', data: [24, 40, 101, 134, 90, 230, 210, 230, 120, 230, 210, 120], type: 'line', smooth: true },{ name:'实际销售额', data: [40, 64, 191, 324, 290, 330, 310, 213, 180, 200, 180, 79], type: 'line', smooth: true } }] 实现步骤：准备切换需要依赖的数据 4组绑定点击事件切换激活 tab 的样式切换图表依赖的数据（重新渲染图表）开启定时器，进行切换， 鼠标经过sales停止定时器，离开开启定时器准备数据，使用数据var data = { year: [ [24, 40, 101, 134, 90, 230, 210, 230, 120, 230, 210, 120], [40, 64, 191, 324, 290, 330, 310, 213, 180, 200, 180, 79] ], quarter: [ [23, 75, 12, 97, 21, 67, 98, 21, 43, 64, 76, 38], [43, 31, 65, 23, 78, 21, 82, 64, 43, 60, 19, 34] ], month: [ [34, 87, 32, 76, 98, 12, 32, 87, 39, 36, 29, 36], [56, 43, 98, 21, 56, 87, 43, 12, 43, 54, 12, 98] ], week: [ [43, 73, 62, 54, 91, 54, 84, 43, 86, 43, 54, 53], [32, 54, 34, 87, 32, 45, 62, 68, 93, 54, 54, 24] ] } series: [{ name:'预期销售额', data: data.year[0], type: 'line', smooth: true, itemStyle: { color: '#00f2f1' } },{ name:'实际销售额', data: data.year[1], type: 'line', smooth: true, itemStyle: { color: '#ed3f35' } }] // 切换 $('.sales').on('click', '.caption a', function(){ // 样式 $(this).addClass('active').siblings().removeClass('active') // currData 当前对应的数据 // this.dataset.type 标签上的data-type属性值，对应data中的属性 var currData = data[this.dataset.type] // 修改图表1的数据 option.series[0].data = currData[0] // 修改图表2的数据 option.series[1].data = currData[1] // 重新设置数据 让图标重新渲染 myChart.setOption(option) }) 开启定时器每隔3s，自动让a触发点击事件即可鼠标经过sales，关闭定时器，离开开启定时器代码示例：var as = $(".sales .caption a"); var index = 0; var timer = setInterval(function() { index++; if (index >= 4) index = 0; as.eq(index).click(); }, 1000); // 鼠标经过sales，关闭定时器，离开开启定时器 $(".sales").hover( function() { clearInterval(timer); }, function() { clearInterval(timer); timer = setInterval(function() { index++; if (index >= 4) index = 0; as.eq(index).click(); }, 1000); } ); 自动缩放效果：// 当我们浏览器缩放的时候，图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); 代码示例： 渠道分布 一季度销售进度 75 % 1,321 销售额(万元) 150% 同比增长 代码示例：/* 渠道区块 */ .wrap { display: flex; } .channel, .quarter { flex: 1; height: 2.9rem; } .channel { margin-right: 0.25rem; } .channel .data { overflow: hidden; } .channel .data .radar { height: 2.1rem; width: 100%; background-color: pink; } .channel h4 { color: #fff; font-size: 0.4rem; margin-bottom: 0.0625rem; } .channel small { font-size: 50%; } .channel span { display: block; color: #4c9bfd; font-size: 0.175rem; } /* 季度区块 */ .quarter .inner { display: flex; flex-direction: column; margin: 0 -0.075rem; } .quarter .chart { flex: 1; padding-top: 0.225rem; } .quarter .box { position: relative; } .quarter .label { transform: translate(-50%, -30%); color: #fff; font-size: 0.375rem; position: absolute; left: 50%; top: 50%; } .quarter .label small { font-size: 50%; } .quarter .gauge { height: 1.05rem; } .quarter .data { display: flex; justify-content: space-between; } .quarter .item { width: 50%; } .quarter h4 { color: #fff; font-size: 0.3rem; margin-bottom: 0.125rem; } .quarter span { display: block; width: 100%; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; color: #4c9bfd; font-size: 0.175rem; } 实现步骤：寻找官方的类似示例，给予分析，并引入到HTML页面中按照需求来定制它代码示例：(function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".radar")); // 2.指定配置 var dataBJ = [[55, 9, 56, 0.46, 18, 6, 1]]; var lineStyle = { normal: { width: 1, opacity: 0.5 } }; var option = { backgroundColor: "#161627", radar: { indicator: [ { name: "AQI", max: 300 }, { name: "PM2.5", max: 250 }, { name: "PM10", max: 300 }, { name: "CO", max: 5 }, { name: "NO2", max: 200 }, { name: "SO2", max: 100 } ], shape: "circle", splitNumber: 5, name: { textStyle: { color: "rgb(238, 197, 102)" } }, splitLine: { lineStyle: { color: [ "rgba(238, 197, 102, 0.1)", "rgba(238, 197, 102, 0.2)", "rgba(238, 197, 102, 0.4)", "rgba(238, 197, 102, 0.6)", "rgba(238, 197, 102, 0.8)", "rgba(238, 197, 102, 1)" ].reverse() } }, splitArea: { show: false }, axisLine: { lineStyle: { color: "rgba(238, 197, 102, 0.5)" } } }, series: [ { name: "北京", type: "radar", lineStyle: lineStyle, data: dataBJ, symbol: "none", itemStyle: { color: "#F9713C" }, areaStyle: { opacity: 0.1 } } ] }; // 3.把配置和数据给对象 myChart.setOption(option); })(); 按照需求来定制它：需求1： 去掉背景颜色，调整雷达图大小 65%radar:{ center: ['50%', '50%'], // 外半径占据容器大小 radius: '65%', } 需求2： 指示器轴的分割段数为4条（4个圆圈）radar:{ center: ['50%', '50%'], // 外半径占据容器大小 radius: '65%', // 指示器轴的分割段数 splitNumber: 4, } 需求3： 雷达图分割线设为白色半透明 0.5 // 坐标轴在 grid 区域中的分隔线（圆圈） splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.5)', // width: 2, // type: 'dashed' } }, 需求4： 雷达图 坐标轴轴线相关设置(竖线) axisLine// 坐标轴轴线相关设置(竖线)axisLine axisLine: { show: true, lineStyle: { color: 'rgba(255, 255, 255, 0.5)' // width: 1, // type: 'solid' } }, 需求5： 修饰雷达图文字颜色为 #4c9bfdname: { // 修饰雷达图文本颜色 textStyle: { color: '#4c9bfd' } }, 需求6： 修饰 区域填充样式 series 对象区域填充的背景颜色设置为： rgba(238, 197, 102, 0.6) areaStyle: { color: 'rgba(238, 197, 102, 0.6)', }, 区域填充的线条颜色为白色// 线条样式 lineStyle: { normal: { color: '#fff', // width: 1 } }, 需求7： 标记的图形(拐点）设置 注意 series 里面设置用圆点显示， 拐点的大小设置为 5小圆点设置为白色在小圆点上显示相关数据，颜色设置为白色，10像素// symbol 标记的样式(拐点），还可以取值'rect' 方块 ,'arrow' 三角等 symbol: 'circle', // 拐点的大小 symbolSize: 5, // 小圆点（拐点）设置为白色 itemStyle: { color: '#fff' }, // 在圆点上显示相关数据 label: { show: true, color: '#fff', fontSize: 10 }, 需求8： 鼠标经过显示提示框组件tooltip: { show: true, // 控制提示框组件的显示位置 position: ['60%', '10%'], }, 需求9： 更换数据 // 雷达图的指示器 内部填充数据 indicator: [ { name: '机场', max: 100 }, { name: '商场', max: 100 }, { name: '火车站', max: 100 }, { name: '汽车站', max: 100 }, { name: '地铁', max: 100 } ], data: [[90, 19, 56, 11, 34]], // 销售渠道模块 雷达图 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".radar")); // 2.指定配置 var option = { tooltip: { show: true, // 控制提示框组件的显示位置 position: ["60%", "10%"] }, radar: { indicator: [ { name: "机场", max: 100 }, { name: "商场", max: 100 }, { name: "火车站", max: 100 }, { name: "汽车站", max: 100 }, { name: "地铁", max: 100 } ], // 修改雷达图的大小 radius: "65%", shape: "circle", // 分割的圆圈个数 splitNumber: 4, name: { // 修饰雷达图文字的颜色 textStyle: { color: "#4c9bfd" } }, // 分割的圆圈线条的样式 splitLine: { lineStyle: { color: "rgba(255,255,255, 0.5)" } }, splitArea: { show: false }, // 坐标轴的线修改为白色半透明 axisLine: { lineStyle: { color: "rgba(255, 255, 255, 0.5)" } } }, series: [ { name: "北京", type: "radar", // 填充区域的线条颜色 lineStyle: { normal: { color: "#fff", width: 1, opacity: 0.5 } }, data: [[90, 19, 56, 11, 34]], // 设置图形标记 （拐点） symbol: "circle", // 这个是设置小圆点大小 symbolSize: 5, // 设置小圆点颜色 itemStyle: { color: "#fff" }, // 让小圆点显示数据 label: { show: true, fontSize: 10 }, // 修饰我们区域填充的背景颜色 areaStyle: { color: "rgba(238, 197, 102, 0.6)" } } ] }; // 3.把配置和数据给对象 myChart.setOption(option); // 当我们浏览器缩放的时候，图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); })(); 实现步骤：寻找官方的类似示例，给予分析,引入到HTML页面中按照需求来定制它。代码示例：// 销售模块 饼形图 半圆形 设置方式 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".gauge")); // 2. 指定数据和配置 var option = { series: [ { name: "销售进度", type: "pie", radius: ["50%", "70%"], //是否启用防止标签重叠策略 // avoidLabelOverlap: false, labelLine: { normal: { show: false } }, data: [{ value: 100 }, { value: 100 }, { value: 200 }] } ] }; // 3. 把数据和配置给实例对象 myChart.setOption(option); })(); 需求1：改成半圆，图表大一些，让50%文字在中心。 var option = { series: [ { type: 'pie', // 放大图形 radius: ['130%', '150%'], // 移动下位置 套住50%文字 center: ['48%', '80%'], label: { normal: { show: false } }, // 起始角度，支持范围[0, 360] startAngle: 180, data: [ { value: 100 }, // 不需要名称 { value: 100,}, // 不需要名称 { value: 200, itemStyle: { color: 'transparent' } } // 透明隐藏第三块区域 ] } ] } 需求2：鼠标经过无需变大，修改第一段颜色渐变#00c9e0->#005fc1，修改第二段颜色#12274d。 // 鼠标经过不变大 hoverOffset: 0, data: [ { value: 100, itemStyle: { // 颜色渐变#00c9e0->#005fc1 color: new echarts.graphic.LinearGradient( // (x1,y2) 点到点 (x2,y2) 之间进行渐变 0, 0, 0, 1, [ { offset: 0, color: "#00c9e0" }, // 0 起始颜色 { offset: 1, color: "#005fc1" } // 1 结束颜色 ] ) } }, { value: 100, itemStyle: { color: '#12274d' } }, // 颜色#12274d 代码示例： 全国热榜 可爱多 娃哈啥 喜之郎 各省热销 // 近30日 // 北京 25,179 河北 23,252 上海 20,760 江苏 23,252 山东 20,760 代码示例：/* 排行榜 */ .top { height: 3.5rem; } .top .inner { display: flex; } .top .all { display: flex; flex-direction: column; width: 2.1rem; color: #4c9bfd; font-size: 0.175rem; vertical-align: middle; } .top .all ul { padding-left: 0.15rem; margin-top: 0.15rem; flex: 1; display: flex; flex-direction: column; justify-content: space-around; } .top .all li { overflow: hidden; } .top .all [class^="icon-"] { font-size: 0.45rem; vertical-align: middle; margin-right: 0.15rem; } .top .province { flex: 1; display: flex; flex-direction: column; color: #fff; } .top .province i { padding: 0 0.15rem; margin-top: 0.0625rem; float: right; font-style: normal; font-size: 0.175rem; color: #0bace6; } .top .province s { display: inline-block; transform: scale(0.8); text-decoration: none; } .top .province .icon-up { color: #dc3c33; } .top .province .icon-down { color: #36be90; } .top .province .data { flex: 1; display: flex; margin-top: 0.175rem; } .top .province ul { flex: 1; line-height: 1; margin-bottom: 0.175rem; } .top .province ul li { display: flex; justify-content: space-between; } .top .province ul span { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .top .province ul.sup { font-size: 0.175rem; } .top .province ul.sup li { color: #4995f4; padding: 0.15rem; } .top .province ul.sup li.active { color: #a3c6f2; background-color: rgba(10, 67, 188, 0.2); } .top .province ul.sub { display: flex; flex-direction: column; justify-content: space-around; font-size: 0.15rem; background-color: rgba(10, 67, 188, 0.2); } .top .province ul.sub li { color: #52ffff; padding: 0.125rem 0.175rem; } .clock { position: absolute; top: -0.45rem; right: 0.5rem; font-size: 0.25rem; color: #0bace6; } .clock i { margin-right: 5px; font-size: 0.25rem; } @media screen and (max-width: 1600px) { .top span { transform: scale(0.9); } .top .province ul.sup li { padding: 0.125rem 0.15rem; } .top .province ul.sub li { padding: 0.0625rem 0.15rem; } .quarter span { transform: scale(0.9); } } 实现思路：准备后台返回的真实数据利用数据渲染各省热销模块 sup 模块 (拼接html格式字符串，进行渲染)当鼠标进入 tab 的时候激活当前的tab样式，删除其他tab的样式渲染各省热销 sub 模块 (拼接html格式字符串，进行渲染)默认激活第一个tab的效果开启定时器，按依次切换预备知识：扩展知识：ES6模版字符代码示例：// 模版字符 var star = { name: "刘德华", age: 18 }; // 以前的写法 拼接的时候引号很容易出问题 console.log("我的名字是" + star.name + "我的年龄是" + star.age); // ES6 模板字符写法 console.log(`我的名字是${star.name}我的年龄是${star.age}`); console.log(`${star.name}${star.age}`); 第一步：得到后台数据（实际开发中，这个数据通过ajax请求获得）代码示例：var hotData = [ { city: '北京', // 城市 sales: '25, 179', // 销售额 flag: true, // 上升还是下降 brands: [ // 品牌种类数据 { name: '可爱多', num: '9,086', flag: true }, { name: '娃哈哈', num: '8,341', flag: true }, { name: '喜之郎', num: '7,407', flag: false }, { name: '八喜', num: '6,080', flag: false }, { name: '小洋人', num: '6,724', flag: false }, { name: '好多鱼', num: '2,170', flag: true }, ] }, { city: '河北', sales: '23,252', flag: false, brands: [ { name: '可爱多', num: '3,457', flag: false }, { name: '娃哈哈', num: '2,124', flag: true }, { name: '喜之郎', num: '8,907', flag: false }, { name: '八喜', num: '6,080', flag: true }, { name: '小洋人', num: '1,724', flag: false }, { name: '好多鱼', num: '1,170', flag: false }, ] }, { city: '上海', sales: '20,760', flag: true, brands: [ { name: '可爱多', num: '2,345', flag: true }, { name: '娃哈哈', num: '7,109', flag: true }, { name: '喜之郎', num: '3,701', flag: false }, { name: '八喜', num: '6,080', flag: false }, { name: '小洋人', num: '2,724', flag: false }, { name: '好多鱼', num: '2,998', flag: true }, ] }, { city: '江苏', sales: '23,252', flag: false, brands: [ { name: '可爱多', num: '2,156', flag: false }, { name: '娃哈哈', num: '2,456', flag: true }, { name: '喜之郎', num: '9,737', flag: true }, { name: '八喜', num: '2,080', flag: true }, { name: '小洋人', num: '8,724', flag: true }, { name: '好多鱼', num: '1,770', flag: false }, ] }, { city: '山东', sales: '20,760', flag: true, brands: [ { name: '可爱多', num: '9,567', flag: true }, { name: '娃哈哈', num: '2,345', flag: false }, { name: '喜之郎', num: '9,037', flag: false }, { name: '八喜', num: '1,080', flag: true }, { name: '小洋人', num: '4,724', flag: false }, { name: '好多鱼', num: '9,999', flag: true }, ] } ] 第二步：根据数据渲染各省热销 sup 模块内容删掉原先自带小li遍历数据 $.each()拼接字符串把数据渲染到 li 的span 里面追加给 .sup 盒子代码示例： var supHTML = ""; $.each(hotData, function(index, item) { // console.log(item); supHTML += ` ${item.city} ${item.sales} `; }); $(".sup").html(supHTML); 第三步：当数据进入 tab 的时候激活当前的tab样式，删除其他tab的样式渲染各省热销 sub 模块注意鼠标进入tab， 只遍历 当前索引号对应的 城市对象里面的 brands拼接html格式字符串，进行渲染；激活当前的tab样式，删除其他tab的样式；渲染各省热销 sub 模块注意鼠标进入tab， 只遍历 当前索引号对应的 城市对象里面的 ；brands拼接html格式字符串，进行渲染； // 所有的LI var $lis = $('.province .sup li') // 第一个默认激活 $lis.eq(0).mouseenter() 定时器里面 mouseenter 冲突问题的解决方案；定时器里面不加mousenter 事件，而是直接重新渲染数据就可以(执行鼠标经过事件里面的代码)最好把渲染的代码封装到函数里面var index = 0; var timer = setInterval(function() { index++; if (index >= 5) index = 0; // lis.eq(index).mouseenter(); render(lis.eq(index)); }, 2000); $(".province .sup").hover( // 鼠标经过事件 function() { clearInterval(timer); }, // 鼠标离开事件 function() { clearInterval(timer); timer = setInterval(function() { index++; if (index >= 5) index = 0; // lis.eq(index).mouseenter(); render(lis.eq(index)); }, 2000); } ); 社区就是一些，活跃的echart使用者，交流和贡献定制好的图表的地方。 在这里可以找到一些基于echart的高度定制好的图表，相当于基于jquery开发的插件，这里是基于echarts开发的第三方的图表。参考社区的例子：https://gallery.echartsjs.com/editor.html?c=x0-ExSkZDM (模拟飞机航线)实现步骤：第一需要下载china.js提供中国地图的js文件第二个因为里面代码比较多，我们新建一个新的js文件 myMap.js 引入使用社区提供的配置即可。需要修改：去掉图例组件和标题组件去掉背景颜色修改地图省份背景 #142957地图放大通过 zoom 设置为1.2即可代码示例：geo: { map: 'china', zoom: 1.2, label: { emphasis: { show: false } }, roam: false, itemStyle: { normal: { areaColor: '#142957', borderColor: '#0692a4' }, emphasis: { areaColor: '#0b1c2d' } } }, 总结：实现一个需求，需要去推导，具备推导的能力需要练习，希望小伙伴们多加练习哦本期推荐内容为jQuery专栏哦！ jQuery-Day1 jQuery-Day2 jQuery-Day3
目录一、选择什么网络模块?二、axios功能特点三、axios支持多种请求方式四、发送并发请求五、全局配置六、创建axios实例七、常见的配置选项八、axios的封装九、封装一个request函数1、传入回调函数2、传入一个参数进行回调3、Promise用法4、简化Promise十、axios中的拦截器```为什么80%的码农做不了架构师？>>>```[Java专栏目录 | 点击这里](https://blog.csdn.net/guorui_java/article/details/120098618)一、选择什么网络模块?发送网络请求的方式有很多，下面来简单介绍一下：1、传统的Ajax是基于XMLHttpRequest（XHR）2、jQuery - Ajax为什么不适用jQuery的Ajax？在vue开发中不需要使用jQuery，因为jQuery很重量级。3、vue官方在Vue1.x的时候，推出了Vue-resource。Vue-resource角jQuery轻便很多，但在vue2.x之后，尤雨溪对Vue-resource不维护了，简言之，就是弃用了。4、尤雨溪推荐使用axios。二、axios功能特点在浏览器中发送XMLHttpRequest请求在node.js中发送http请求支持Promise API支持拦截请求和响应转换请求和响应数据三、axios支持多种请求方式axios(config)axios.request(config)axios.get(url,[,config])axios.delete(url,[,config])axios.head(url,[,config])axios.post(url,[,data[, config]])axios.put(url,[,data[, config]])axios.patch(url,[,data[, config]])四、发送并发请求有的时候，会同时发送多个请求。使用axios.all，可以放入多个请求的数组。axios.all([])返回的是一个数组，使用axios.spread可以将数组[res1,res2]展开为res1和res2。import axios from 'axios' export default { name: 'app', created(){ axios.all([axios.get('http://127.0.0.1:8080/getUserList'), axios.get('http://127.0.0.1:8080/getUserPage',{ params: {pageNum: 1, pageSize: 10} }) ]).then(axios.spread((res1,res2) => { console.log(res1) console.log(res2) })) } } 五、全局配置import axios from 'axios' export default { name: 'app', created(){ // 提取全局配置 axios.defaults.baseURL = 'http://127.0.0.1:8080' axios.all([axios.get('/getUserList'), axios.get('/getUserPage',{ params: {pageNum: 1, pageSize: 10} }) ]).then(axios.spread((res1,res2) => { console.log(res1) console.log(res2) })) } } 六、创建axios实例const instance1 = axios.create({ baseURL: 'http://127.0.0.1:8080', timeout: 5000 }) instance1({ url: '/home/getUserList' }).then(res => { console.log(res); }) instance1({ url: '/home/getUserPage', params: { type: 'pop', page: 1 } }).then(res => { console.log(res); }) const instance2 = axios.create({ baseURL: 'http://127.0.0.1:8081', timeout: 10000, // headers: {} }) 七、常见的配置选项1、请求地址\url:'http://127.0.0.1:8080/getUserList'2、请求类型method:'get'3、请求路径baseURL:'http://127.0.0.1:8080'4、请求前的数据处理transformRequest:[function(data){}],5、请求后的数据处理transformResponse: [function(data){}],6、自定义的请求头headers:{'x-Requested-With':'XMLHttpRequest'},7、URL查询对象params:{ id: 1 },8、查询对象序列化函数paramsSerializer: function(params){ }9、request bodydata: { key: 'aa'},10、超时设置stimeout: 5000,11、跨域是否带TokenwithCredentials: false,12、自定义请求处理adapter: function(resolve, reject, config){},13、身份验证信息auth: { uname: '', pwd: '12'},14、响应的数据格式 json / blob /document /arraybuffer / text / streamresponseType: 'json',八、axios的封装import axios from 'axios' export default function axios(option){ return new Promise((resolve,reject) => { //1.创建sxios实例 const instance = axios.create({ url: 'api', timeout: 5000, headers: '' }) //2.传入对象进行网络请求 instance(option).then(res => { resolve(res) }).catch(err => { reject(err) }) }) } 九、封装一个request函数1、传入回调函数export function request(config, success, failure) { // 1.创建axios的实例 const instance = axios.create({ baseURL: 'http://127.0.0.1:8080', timeout: 5000 }) // 发送真正的网络请求 instance(config) .then(res => { success(res); }) .catch(err => { failure(err) }) } 调用封装好的request模块import {request} from "./network/request"; request({ url: '/home/multidata' }, res => { console.log(res); }, err => { console.log(err); }) 2、传入一个参数进行回调export function request(config) { // 1.创建axios的实例 const instance = axios.create({ baseURL: 'http://127.0.0.1:8080', timeout: 5000 }) // 发送真正的网络请求 instance(config.baseConfig) .then(res => { config.success(res); }) .catch(err => { config.failure(err) }) } import {request} from "./network/request"; request({ baseConfig: { }, success: function (res) { }, failure: function (err) { } }) 3、Promise用法export function request(config) { return new Promise((resolve, reject) => { // 1.创建axios的实例 const instance = axios.create({ baseURL: 'http://127.0.0.1:8080', timeout: 5000 }) // 发送真正的网络请求 instance(config) .then(res => { resolve(res) }) .catch(err => { reject(err) }) }) } request({ url: '/home/multidata' }).then(res => { console.log(res); }).catch(err => { // console.log(err); }) 4、简化Promiseexport function request(config) { return new Promise((resolve, reject) => { // 1.创建axios的实例 const instance = axios.create({ baseURL: 'http://127.0.0.1:8080', timeout: 5000 }) // 发送真正的网络请求 return instance(config) }) } 十、axios中的拦截器请求拦截的作用是什么？比如config中的一些信息不符合服务器的要求比如每次发送网络请求时, 都希望在界面中显示一个请求的图标某些网络请求(比如登录(token)), 必须携带一些特殊的信息import axios from 'axios'export function request(config) { // 1.创建axios的实例 const instance = axios.create({baseURL: 'http://127.0.0.1:8080', timeout: 5000})// 2.axios的拦截器 // 2.1.请求拦截的作用 instance.interceptors.request.use(config => {// console.log(config); return config}, err => {// console.log(err);})// 2.2.响应拦截 instance.interceptors.response.use(res => {// console.log(res); return res.data}, err => {console.log(err);})// 3.发送真正的网络请求 return instance(config)}