首页
友链
统计
留言
更多
直播
壁纸
推荐
我的毛线
哔哔点啥
Search
1
pxe 自动化安装系统
660 阅读
2
本站官方群:894703859------|诚邀各位大佬的入驻!
650 阅读
3
软件安装
471 阅读
4
新款螺旋帽子编织#夏凉帽#合股线夏凉帽编织
405 阅读
5
10 个Linux Awk文本处理经典案例
376 阅读
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
登录
/
注册
Search
标签搜索
基础
js
Nginx
css
webapi
jQuery
面向对象
command
项目
ansible
用户权限
go
html
文件管理
命令
k8s
shell
pxe
awk
vscode
JustDoIt
累计撰写
114
篇文章
累计收到
4
条评论
首页
栏目
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
页面
友链
统计
留言
直播
壁纸
推荐
我的毛线
哔哔点啥
搜索到
53
篇与
的结果
2022-09-20
Vue 是什么?
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架 vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合 使用Vue将helloworld 渲染到页面上指令 本质就是自定义属性 Vue中指定都是以 v- 开头 v-cloak 防止页面加载时出现闪烁问题 <style type="text/css"> /* 1、通过属性选择器 选择到 带有属性 v-cloak的标签 让他隐藏 */ [v-cloak]{ /* 元素隐藏 */ display: none; } </style> <body> <div id="app"> <!-- 2、 让带有插值 语法的 添加 v-cloak 属性 在 数据渲染完场之后,v-cloak 属性会被自动去除, v-cloak一旦移除也就是没有这个属性了 属性选择器就选择不到该标签 也就是对应的标签会变为可见 --> <div v-cloak >{{msg}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ // el 指定元素 id 是 app 的元素 el: '#app', // data 里面存储的是数据 data: { msg: 'Hello Vue' } }); </script> </body> </html> v-text v-text指令用于将数据填充到标签中,作用于插值表达式类似,但是没有闪动问题 如果数据中有HTML标签会将html标签一并输出 注意:此处为单向绑定,数据对象上的值改变,插值会发生变化;但是当插值发生变化并不会影响数据对象的值 <div id="app"> <!-- 注意:在指令中不要写插值语法 直接写对应的变量名称 在 v-text 中 赋值的时候不要在写 插值语法 一般属性中不加 {{}} 直接写 对应 的数据名 --> <p v-text="msg"></p> <p> <!-- Vue 中只有在标签的 内容中 才用插值语法 --> {{msg}} </p> </div> <script> new Vue({ el: '#app', data: { msg: 'Hello Vue.js' } }); </script> v-html 用法和v-text 相似 但是他可以将HTML片段填充到标签中 可能有安全问题, 一般只在可信任内容上使用 v-html,永不用在用户提交的内容上 它与v-text区别在于v-text输出的是纯文本,浏览器不会对其再进行html解析,但v-html会将其当html标签解析后输出。<div id="app"> <p v-html="html"></p> <!-- 输出:html标签在渲染的时候被解析 --> <p>{{message}}</p> <!-- 输出:<span>通过双括号绑定</span> --> <p v-text="text"></p> <!-- 输出:<span>html标签在渲染的时候被源码输出</span> --> </div> <script> let app = new Vue({ el: "#app", data: { message: "<span>通过双括号绑定</span>", html: "<span>html标签在渲染的时候被解析</span>", text: "<span>html标签在渲染的时候被源码输出</span>", } }); </script> v-pre 显示原始信息跳过编译过程 跳过这个元素和它的子元素的编译过程。 一些静态的内容不需要编译加这个指令可以加快渲染 <span v-pre>{{ this will not be compiled }}</span> <!-- 显示的是{{ this will not be compiled }} --> <span v-pre>{{msg}}</span> <!-- 即使data里面定义了msg这里仍然是显示的{{msg}} --> <script> new Vue({ el: '#app', data: { msg: 'Hello Vue.js' } }); </script> v-once 执行一次性的插值【当数据改变时,插值处的内容不会继续更新】 <!-- 即使data里面定义了msg 后期我们修改了 仍然显示的是第一次data里面存储的数据即 Hello Vue.js --> <span v-once>{{ msg}}</span> <script> new Vue({ el: '#app', data: { msg: 'Hello Vue.js' } }); </script> 双向数据绑定 当数据发生变化的时候,视图也就发生变化 当视图发生变化的时候,数据也会跟着同步变化 v-model v-model是一个指令,限制在 <input>、<select>、<textarea>、components中使用 <div id="app"> <div>{{msg}}</div> <div> 当输入框中内容改变的时候, 页面上的msg 会自动更新 <input type="text" v-model='msg'> </div> </div> mvvm MVC 是后端的分层开发概念; MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel m model 数据层 Vue 中 数据层 都放在 data 里面 v view 视图 Vue 中 view 即 我们的HTML页面 vm (view-model) 控制器 将数据和视图层建立联系 vm 即 Vue 的实例 就是 vm v-on 用来绑定事件的 形式如:v-on:click 缩写为 @click; v-on事件函数中传入参数 <body> <div id="app"> <div>{{num}}</div> <div> <!-- 如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 --> <button v-on:click='handle1'>点击1</button> <!-- 2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递, 并且事件对象的名称必须是$event --> <button v-on:click='handle2(123, 456, $event)'>点击2</button> </div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { num: 0 }, methods: { handle1: function(event) { console.log(event.target.innerHTML) }, handle2: function(p, p1, event) { console.log(p, p1) console.log(event.target.innerHTML) this.num++; } } }); </script> 事件修饰符 在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。 Vue 不推荐我们操作DOM 为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符 修饰符是由点开头的指令后缀来表示的 <!-- 阻止单击事件继续传播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 即阻止冒泡也阻止默认事件 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div> 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。 按键修饰符 在做项目中有时会用到键盘事件,在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符 <!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` --> <input v-on:keyup.13="submit"> <!-- -当点击enter 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit"> <!--当点击enter或者space时 时调用 `vm.alertMe()` --> <input type="text" v-on:keyup.enter.space="alertMe" > 常用的按键修饰符 .enter => enter键 .tab => tab键 .delete (捕获“删除”和“退格”按键) => 删除键 .esc => 取消键 .space => 空格键 .up => 上 .down => 下 .left => 左 .right => 右 <script> var vm = new Vue({ el:"#app", methods: { submit:function(){}, alertMe:function(){}, } }) </script> 自定义按键修饰符别名 在Vue中可以通过config.keyCodes自定义按键修饰符别名 <div id="app"> 预先定义了keycode 116(即F5)的别名为f5,因此在文字输入框中按下F5,会触发prompt方法 <input type="text" v-on:keydown.f5="prompt()"> </div> <script> Vue.config.keyCodes.f5 = 116; let app = new Vue({ el: '#app', methods: { prompt: function() { alert('我是 F5!'); } } }); </script> v-bind v-bind 指令被用来响应地更新 HTML 属性 v-bind:href 可以缩写为 :href; <!-- 绑定一个属性 --> <img v-bind:src="imageSrc"> <!-- 缩写 --> <img :src="imageSrc"> 绑定对象 我们可以给v-bind:class 一个对象,以动态地切换class。 注意:v-bind:class指令可以与普通的class特性共存 1、 v-bind 中支持绑定一个对象 如果绑定的是一个对象 则 键为 对应的类名 值 为对应data中的数据 <!-- HTML最终渲染为 <ul class="box textColor textSize"></ul> 注意: textColor,textSize 对应的渲染到页面上的CSS类名 isColor,isSize 对应vue data中的数据 如果为true 则对应的类名 渲染到页面上 当 isColor 和 isSize 变化时,class列表将相应的更新, 例如,将isSize改成false, class列表将变为 <ul class="box textColor"></ul> --> <ul class="box" v-bind:class="{textColor:isColor, textSize:isSize}"> <li>学习Vue</li> <li>学习Node</li> <li>学习React</li> </ul> <div v-bind:style="{color:activeColor,fontSize:activeSize}">对象语法</div> <sript> var vm= new Vue({ el:'.box', data:{ isColor:true, isSize:true, activeColor:"red", activeSize:"25px", } }) </sript> <style> .box{ border:1px dashed #f0f; } .textColor{ color:#f00; background-color:#eef; } .textSize{ font-size:30px; font-weight:bold; } </style> 绑定class2、 v-bind 中支持绑定一个数组 数组中classA和 classB 对应为data中的数据 这里的classA 对用data 中的 classA 这里的classB 对用data 中的 classB <ul class="box" :class="[classA, classB]"> <li>学习Vue</li> <li>学习Node</li> <li>学习React</li> </ul> <script> var vm= new Vue({ el:'.box', data:{ classA:‘textColor‘, classB:‘textSize‘ } }) </script> <style> .box{ border:1px dashed #f0f; } .textColor{ color:#f00; background-color:#eef; } .textSize{ font-size:30px; font-weight:bold; } </style> 绑定对象和绑定数组 的区别 绑定对象的时候 对象的属性 即要渲染的类名 对象的属性值对应的是 data 中的数据 绑定数组的时候数组里面存的是data 中的数据 绑定style <div v-bind:style="styleObject">绑定样式对象</div>' <!-- CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) --> <div v-bind:style="{ color: activeColor, fontSize: fontSize,background:'red' }">内联样式</div> <!--组语法可以将多个样式对象应用到同一个元素 --> <div v-bind:style="[styleObj1, styleObj2]"></div> <script> new Vue({ el: '#app', data: { styleObject: { color: 'green', fontSize: '30px', background:'red' }, activeColor: 'green', fontSize: "30px" }, styleObj1: { color: 'red' }, styleObj2: { fontSize: '30px' } </script> 分支结构v-if 使用场景 1- 多个元素 通过条件判断展示或者隐藏某个元素。或者多个元素 2- 进行两个视图之间的切换 <div id="app"> <!-- 判断是否加载,如果为真,就加载,否则不加载--> <span v-if="flag"> 如果flag为true则显示,false不显示! </span> </div> <script> var vm = new Vue({ el:"#app", data:{ flag:true } }) </script> ---------------------------------------------------------- <div v-if="type === 'A'"> A </div> <!-- v-else-if紧跟在v-if或v-else-if之后 表示v-if条件不成立时执行--> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <!-- v-else紧跟在v-if或v-else-if之后--> <div v-else> Not A/B/C </div> <script> new Vue({ el: '#app', data: { type: 'C' } }) </script> v-show 和 v-if的区别 v-show本质就是标签display设置为none,控制隐藏 v-show只编译一次,后面其实就是控制css,而v-if不停的销毁和创建,故v-show性能更好一点。 v-if是动态的向DOM树内添加或者删除DOM元素 v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件 循环结构v-for 用于循环的数组里面的值可以是对象,也可以是普通元素 <ul id="example-1"> <!-- 循环结构-遍历数组 item 是我们自己定义的一个名字 代表数组里面的每一项 items对应的是 data中的数组--> <li v-for="item in items"> {{ item.message }} </li> </ul> <script> new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ], } }) </script> 不推荐同时使用 v-if 和 v-for 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。 <!-- 循环结构-遍历对象 v 代表 对象的value k 代表对象的 键 i 代表索引 ---> <div v-if='v==13' v-for='(v,k,i) in obj'>{{v + '---' + k + '---' + i}}</div> <script> new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ], obj: { uname: 'zhangsan', age: 13, gender: 'female' } } }) </script> key 的作用 key来给每个节点做一个唯一标识 key的作用主要是为了高效的更新虚拟DOM <ul> <li v-for="item in items" :key="item.id">...</li> </ul> 案例选项卡1、 HTML 结构` <div id="app"> <div class="tab"> <!-- tab栏 --> <ul> <li class="active">apple</li> <li class="">orange</li> <li class="">lemon</li> </ul> <!-- 对应显示的图片 --> <div class="current"><img src="img/apple.png"></div> <div class=""><img src="img/orange.png"></div> <div class=""><img src="img/lemon.png"></div> </div> </div> ` 2、 提供的数据 list: [{ id: 1, title: 'apple', path: 'img/apple.png' }, { id: 2, title: 'orange', path: 'img/orange.png' }, { id: 3, title: 'lemon', path: 'img/lemon.png' }] 3、 把数据渲染到页面 把tab栏 中的数替换到页面上 把 data 中 title 利用 v-for 循环渲染到页面上 把 data 中 path利用 v-for 循环渲染到页面上 <div id="app"> <div class="tab"> <ul> <!-- 1、绑定key的作用 提高Vue的性能 2、 key 需要是唯一的标识 所以需要使用id, 也可以使用index , index 也是唯一的 3、 item 是 数组中对应的每一项 4、 index 是 每一项的 索引 --> <li :key='item.id' v-for='(item,index) in list'>{{item.title}}</li> </ul> <div :key='item.id' v-for='(item, index) in list'> <!-- : 是 v-bind 的简写 绑定属性使用 v-bind --> <img :src="item.path"> </div> </div> </div> <script> new Vue({ // 指定 操作元素 是 id 为app 的 el: '#app', data: { list: [{ id: 1, title: 'apple', path: 'img/apple.png' }, { id: 2, title: 'orange', path: 'img/orange.png' }, { id: 3, title: 'lemon', path: 'img/lemon.png' }] } }) </script> 4、 给每一个tab栏添加事件,并让选中的高亮 4.1 、让默认的第一项tab栏高亮 tab栏高亮 通过添加类名active 来实现 (CSS active 的样式已经提前写好) 在data 中定义一个 默认的 索引 currentIndex 为 0 给第一个li 添加 active 的类名 通过动态绑定class 来实现 第一个li 的索引为 0 和 currentIndex 的值刚好相等 currentIndex === index 如果相等 则添加类名 active 否则 添加 空类名 4.2 、让默认的第一项tab栏对应的div 显示 实现思路 和 第一个 tab 实现思路一样 只不过 这里控制第一个div 显示的类名是 current <ul> <!-- 动态绑定class 有 active 类名高亮 无 active 不高亮--> <li :class='currentIndex==index?"active":""' :key='item.id' v-for='(item,index) in list' >{{item.title}}</li> </ul> <!-- 动态绑定class 有 current 类名显示 无 current 隐藏--> <div :class='currentIndex==index?"current":""' :key='item.id' v-for='(item, index) in list'> <!-- : 是 v-bind 的简写 绑定属性使用 v-bind --> <img :src="item.path"> </div> <script> new Vue({ el: '#app', data: { currentIndex: 0, // 选项卡当前的索引 默认为 0 list: [{ id: 1, title: 'apple', path: 'img/apple.png' }, { id: 2, title: 'orange', path: 'img/orange.png' }, { id: 3, title: 'lemon', path: 'img/lemon.png' }] } }) </script> 4.3 、点击每一个tab栏 当前的高亮 其他的取消高亮 给每一个li添加点击事件 让当前的索引 index 和 当前 currentIndex 的 值 进项比较 如果相等 则当前li 添加active 类名 当前的 li 高亮 当前对应索引的 div 添加 current 当前div 显示 其他隐藏 <div id="app"> <div class="tab"> <ul> <!-- 通过v-on 添加点击事件 需要把当前li 的索引传过去 --> <li v-on:click='change(index)' :class='currentIndex==index?"active":""' :key='item.id' v-for='(item,index) in list'>{{item.title}}</li> </ul> <div :class='currentIndex==index?"current":""' :key='item.id' v-for='(item, index) in list'> <img :src="item.path"> </div> </div> </div> <script> new Vue({ el: '#app', data: { currentIndex: 0, // 选项卡当前的索引 默认为 0 list: [{ id: 1, title: 'apple', path: 'img/apple.png' }, { id: 2, title: 'orange', path: 'img/orange.png' }, { id: 3, title: 'lemon', path: 'img/lemon.png' }] }, methods: { change: function(index) { // 通过传入过来的索引来让当前的 currentIndex 和点击的index 值 相等 // 从而实现 控制类名 this.currentIndex = index; } } }) </script>
2022年09月20日
140 阅读
0 评论
1 点赞
2022-09-04
基础使用方法js
1. 基本使用步骤 在 <head> 中导入 cropper.css 样式表:<link rel="stylesheet" href="/assets/lib/cropper/cropper.css" /> 在 <body> 的结束标签之前,按顺序导入如下的 js 脚本:<script src="/assets/lib/jquery.js"></script> <script src="/assets/lib/cropper/Cropper.js"></script> <script src="/assets/lib/cropper/jquery-cropper.js"></script> 在卡片的 layui-card-body 主体区域中,定义如下的 HTML 结构: <!-- 第一行的图片裁剪和预览区域 --> <div class="row1"> <!-- 图片裁剪区域 --> <div class="cropper-box"> <!-- 这个 img 标签很重要,将来会把它初始化为裁剪区域 --> <img id="image" src="/assets/images/sample.jpg" /> </div> <!-- 图片的预览区域 --> <div class="preview-box"> <div> <!-- 宽高为 100px 的预览区域 --> <div class="img-preview w100"></div> <p class="size">100 x 100</p> </div> <div> <!-- 宽高为 50px 的预览区域 --> <div class="img-preview w50"></div> <p class="size">50 x 50</p> </div> </div> </div> <!-- 第二行的按钮区域 --> <div class="row2"> <button type="button" class="layui-btn">上传</button> <button type="button" class="layui-btn layui-btn-danger">确定</button> </div> 美化的样式:/* 设置卡片主体区域的宽度 */ .layui-card-body { width: 500px; } /* 设置按钮行的样式 */ .row2 { display: flex; justify-content: flex-end; margin-top: 20px; } /* 设置裁剪区域的样式 */ .cropper-box { width: 350px; height: 350px; background-color: cyan; overflow: hidden; } /* 设置第一个预览区域的样式 */ .w100 { width: 100px; height: 100px; background-color: gray; } /* 设置第二个预览区域的样式 */ .w50 { width: 50px; height: 50px; background-color: gray; margin-top: 50px; } /* 设置预览区域下方文本的样式 */ .size { font-size: 12px; color: gray; text-align: center; } /* 设置图片行的样式 */ .row1 { display: flex; } /* 设置 preview-box 区域的的样式 */ .preview-box { display: flex; flex-direction: column; flex: 1; align-items: center; } /* 设置 img-preview 区域的样式 */ .img-preview { overflow: hidden; border-radius: 50%; } 实现基本裁剪效果: // 1.1 获取裁剪区域的 DOM 元素 var $image = $('#image') // 1.2 配置选项 const options = { // 纵横比 aspectRatio: 1, // 指定预览区域 preview: '.img-preview' } // 1.3 创建裁剪区域 $image.cropper(options) 2. 更换裁剪的图片 拿到用户选择的文件var file = e.target.files[0] 根据选择的文件,创建一个对应的 URL 地址:var newImgURL = URL.createObjectURL(file) 先销毁旧的裁剪区域,再重新设置图片路径,之后再创建新的裁剪区域:$image .cropper('destroy') // 销毁旧的裁剪区域 .attr('src', newImgURL) // 重新设置图片路径 .cropper(options) // 重新初始化裁剪区域 3. 将裁剪后的图片,输出为 base64 格式的字符串var dataURL = $image .cropper('getCroppedCanvas', { // 创建一个 Canvas 画布 width: 100, height: 100 }) .toDataURL('image/png') // 将 Canvas 画布上的内容,转化为 base64 格式的字符串
2022年09月04日
66 阅读
0 评论
0 点赞
2022-07-10
ES6语法
ES6相关概念(★★)什么是ES6ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。为什么使用 ES6 ?每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方。 变量提升特性增加了程序运行时的不可预测性 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码 ES6新增语法let(★★★)ES6中新增了用于声明变量的关键字let声明的变量只在所处于的块级有效 if (true) { let a = 10; } console.log(a) // a is not defined **注意:**使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。不存在变量提升console.log(a); // a is not defined let a = 20; 暂时性死区利用let声明的变量会绑定在这个块级作用域,不会受外界的影响 var tmp = 123; if (true) { tmp = 'abc'; let tmp; } 经典面试题 var arr = []; for (var i = 0; i < 2; i++) { arr[i] = function () { console.log(i); } } arr[0](); arr[1](); **经典面试题图解:**此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。 let arr = []; for (let i = 0; i < 2; i++) { arr[i] = function () { console.log(i); } } arr[0](); arr[1](); **经典面试题图解:**此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.小结 let关键字就是用来声明变量的 使用let关键字声明的变量具有块级作用域 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的 防止循环变量变成全局变量 使用let关键字声明的变量没有变量提升 使用let关键字声明的变量具有暂时性死区特性 const(★★★)声明常量,常量就是值(内存地址)不能变化的量具有块级作用域 if (true) { const a = 10; } console.log(a) // a is not defined 声明常量时必须赋值const PI; // Missing initializer in const declaration 常量赋值后,值不能修改const PI = 3.14; PI = 100; // Assignment to constant variable. const ary = [100, 200]; ary[0] = 'a'; ary[1] = 'b'; console.log(ary); // ['a', 'b']; ary = ['a', 'b']; // Assignment to constant variable. 小结 const声明的变量是一个常量 既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值 声明 const时候必须要给定值 let、const、var 的区别 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值 解构赋值(★★★)ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构数组解构 let [a, b, c] = [1, 2, 3]; console.log(a)//1 console.log(b)//2 console.log(c)//3 //如果解构不成功,变量的值为undefined 对象解构 let person = { name: 'zhangsan', age: 20 }; let { name, age } = person; console.log(name); // 'zhangsan' console.log(age); // 20 let {name: myName, age: myAge} = person; // myName myAge 属于别名 console.log(myName); // 'zhangsan' console.log(myAge); // 20 小结 解构赋值就是把数据结构分解,然后给变量进行赋值 如果结构不成功,变量跟数值个数不匹配的时候,变量的值为undefined 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开 利用解构赋值能够让我们方便的去取对象中的属性跟方法 箭头函数(★★★)ES6中新增的定义函数的方式。() => {} //():代表是函数; =>:必须要的符号,指向哪一个代码块;{}:函数体 const fn = () => {}//代表把一个函数赋值给fn 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号 function sum(num1, num2) { return num1 + num2; } //es6写法 const sum = (num1, num2) => num1 + num2; 如果形参只有一个,可以省略小括号 function fn (v) { return v; } //es6写法 const fn = v => v; 箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文thisconst obj = { name: '张三'} function fn () { console.log(this);//this 指向 是obj对象 return () => { console.log(this);//this 指向 的是箭头函数定义的位置,那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象 } } const resFn = fn.call(obj); resFn(); 小结 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置,可以简单理解成,定义箭头函数中的作用域的this指向谁,它就指向谁 箭头函数的优点在于解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题 面试题var age = 100; var obj = { age: 20, say: () => { alert(this.age) } } obj.say();//箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域 剩余参数(★★)剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数function sum (first, ...args) { console.log(first); // 10 console.log(args); // [20, 30] } sum(10, 20, 30) 剩余参数和解构配合使用let students = ['wangwu', 'zhangsan', 'lisi']; let [s1, ...s2] = students; console.log(s1); // 'wangwu' console.log(s2); // ['zhangsan', 'lisi'] ES6 的内置对象扩展Array 的扩展方法(★★)扩展运算符(展开语法)扩展运算符可以将数组或者对象转为用逗号分隔的参数序列 let ary = [1, 2, 3]; ...ary // 1, 2, 3 console.log(...ary); // 1 2 3,相当于下面的代码 console.log(1,2,3); 扩展运算符可以应用于合并数组// 方法一 let ary1 = [1, 2, 3]; let ary2 = [3, 4, 5]; let ary3 = [...ary1, ...ary2]; // 方法二 ary1.push(...ary2); 将类数组或可遍历对象转换为真正的数组let oDivs = document.getElementsByTagName('div'); oDivs = [...oDivs]; 构造函数方法:Array.from()将伪数组或可遍历对象转换为真正的数组//定义一个集合 let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; //转成数组 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'] 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组 let arrayLike = { "0": 1, "1": 2, "length": 2 } let newAry = Array.from(arrayLike, item => item *2)//[2,4] 注意:如果是对象,那么属性需要写对应的索引实例方法:find()用于找出第一个符合条件的数组成员,如果没有找到返回undefinedlet ary = [{ id: 1, name: '张三' }, { id: 2, name: '李四' }]; let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个 实例方法:findIndex()用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1let ary = [1, 5, 10, 15]; let index = ary.findIndex((value, index) => value > 9); console.log(index); // 2 实例方法:includes()判断某个数组是否包含给定的值,返回布尔值。[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false String 的扩展方法模板字符串(★★★)ES6新增的创建字符串的方式,使用反引号定义let name = `zhangsan`; 模板字符串中可以解析变量let name = '张三'; let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan 模板字符串中可以换行 let result = { name: 'zhangsan', age: 20, sex: '男' } let html = ` <div> <span>${result.name}</span> <span>${result.age}</span> <span>${result.sex}</span> </div> `; 在模板字符串中可以调用函数const sayHello = function () { return '哈哈哈哈 追不到我吧 我就是这么强大'; }; let greet = `${sayHello()} 哈哈哈哈`; console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈 实例方法:startsWith() 和 endsWith() startsWith():表示参数字符串是否在原字符串的头部,返回布尔值 endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值 let str = 'Hello world!'; str.startsWith('Hello') // true str.endsWith('!') // true 实例方法:repeat()repeat方法表示将原字符串重复n次,返回一个新字符串'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" Set 数据结构(★★)ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成 Set 数据结构const s = new Set(); Set函数可以接受一个数组作为参数,用来初始化。const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4} 实例方法 add(value):添加某个值,返回 Set 结构本身 delete(value):删除某个值,返回一个布尔值,表示删除是否成功 has(value):返回一个布尔值,表示该值是否为 Set 的成员 clear():清除所有成员,没有返回值 const s = new Set(); s.add(1).add(2).add(3); // 向 set 结构中添加值 s.delete(2) // 删除 set 结构中的2值 s.has(1) // 表示 set 结构中是否有1这个值 返回布尔值 s.clear() // 清除 set 结构中的所有值 //注意:删除的是元素的值,不是代表的索引 遍历Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。s.forEach(value => console.log(value))
2022年07月10日
112 阅读
0 评论
0 点赞
2022-07-10
正则表达式概述
1.1什么是正则表达式正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。其他语言也会使用正则表达式,本阶段我们主要是利用JavaScript 正则表达式完成表单验证。1.2 正则表达式的特点 灵活性、逻辑性和功能性非常的强。 可以迅速地用极简单的方式达到字符串的复杂控制。 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$ 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /^[a-z0-9_-]{3,16}$/ 2.正则表达式在js中的使用2.1正则表达式的创建在 JavaScript 中,可以通过两种方式创建一个正则表达式。方式一:通过调用RegExp对象的构造函数创建var regexp = new RegExp(/123/); console.log(regexp); 方式二:利用字面量创建 正则表达式 var rg = /123/; 2.2测试正则表达式test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。var rg = /123/; console.log(rg.test(123));//匹配字符中是否出现123 出现结果为true console.log(rg.test('abc'));//匹配字符中是否出现123 未出现结果为false 3.正则表达式中的特殊字符3.1正则表达式的组成一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。特殊字符非常多,可以参考:MDNjQuery 手册:正则表达式部分[正则测试工具]( <http://tool.oschina.net/regex)3.2边界符正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符 边界符 说明 ^ 表示匹配行首的文本(以谁开始) $ 表示匹配行尾的文本(以谁结束) 如果 ^和 $ 在一起,表示必须是精确匹配。var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型 // /abc/ 只要包含有abc这个字符串返回的都是true console.log(rg.test('abc')); console.log(rg.test('abcd')); console.log(rg.test('aabcd')); console.log('---------------------------'); var reg = /^abc/; console.log(reg.test('abc')); // true console.log(reg.test('abcd')); // true console.log(reg.test('aabcd')); // false console.log('---------------------------'); var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范 console.log(reg1.test('abc')); // true console.log(reg1.test('abcd')); // false console.log(reg1.test('aabcd')); // false console.log(reg1.test('abcabc')); // false 3.3字符类字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。3.3.1 [] 方括号表示有一系列字符可供选择,只要匹配其中一个就可以了var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true console.log(rg.test('andy'));//true console.log(rg.test('baby'));//true console.log(rg.test('color'));//true console.log(rg.test('red'));//false var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b 或者是c 这三个字母才返回 true console.log(rg1.test('aa'));//false console.log(rg1.test('a'));//true console.log(rg1.test('b'));//true console.log(rg1.test('c'));//true console.log(rg1.test('abc'));//true ---------------------------------------------------------------------------------- var reg = /^[a-z]$/ //26个英文字母任何一个字母返回 true - 表示的是a 到z 的范围 console.log(reg.test('a'));//true console.log(reg.test('z'));//true console.log(reg.test('A'));//false ----------------------------------------------------------------------------------- //字符组合 var reg1 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true ------------------------------------------------------------------------------------ //取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。 var reg2 = /^[^a-zA-Z0-9]$/; console.log(reg2.test('a'));//false console.log(reg2.test('B'));//false console.log(reg2.test(8));//false console.log(reg2.test('!'));//true 3.3.2量词符量词符用来设定某个模式出现的次数。 量词 说明 * 重复0次或更多次 + 重复1次或更多次 ? 重复0次或1次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 3.3.3用户名表单验证功能需求: 如果用户名输入合法, 则后面提示信息为: 用户名合法,并且颜色为绿色 如果用户名输入不合法, 则后面提示信息为: 用户名不符合规范, 并且颜色为红色 分析: 用户名只能为英文字母,数字,下划线或者短横线组成, 并且用户名长度为6~16位. 首先准备好这种正则表达式模式/$[a-zA-Z0-9-_]{6,16}^/ 当表单失去焦点就开始验证. 如果符合正则规范, 则让后面的span标签添加 right类. 如果不符合正则规范, 则让后面的span标签添加 wrong类. <input type="text" class="uname"> <span>请输入用户名</span> <script> // 量词是设定某个模式出现的次数 var reg = /^[a-zA-Z0-9_-]{6,16}$/; // 这个模式用户只能输入英文字母 数字 下划线 中划线 var uname = document.querySelector('.uname'); var span = document.querySelector('span'); uname.onblur = function() { if (reg.test(this.value)) { console.log('正确的'); span.className = 'right'; span.innerHTML = '用户名格式输入正确'; } else { console.log('错误的'); span.className = 'wrong'; span.innerHTML = '用户名格式输入不正确'; } } </script> 3.3.4 括号总结1.大括号 量词符. 里面表示重复次数2.中括号 字符集合。匹配方括号中的任意字符.3.小括号表示优先级正则表达式在线测试3.4预定义类预定义类指的是某些常见模式的简写方式.案例:验证座机号码var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/; var reg = /^\d{3,4}-\d{7,8}$/; 表单验证案例//手机号验证:/^1[3|4|5|7|8][0-9]{9}$/; //验证通过与不通过更换元素的类名与元素中的内容 if (reg.test(this.value)) { // console.log('正确的'); this.nextElementSibling.className = 'success'; this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确'; } else { // console.log('不正确'); this.nextElementSibling.className = 'error'; this.nextElementSibling.innerHTML = '<i class="error_icon"></i>格式不正确,请从新输入 '; } //QQ号验证: /^[1-9]\d{4,}$/; //昵称验证:/^[\u4e00-\u9fa5]{2,8}$/ //验证通过与不通过更换元素的类名与元素中的内容 ,将上一步的匹配代码进行封装,多次调用即可 function regexp(ele, reg) { ele.onblur = function() { if (reg.test(this.value)) { // console.log('正确的'); this.nextElementSibling.className = 'success'; this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确'; } else { // console.log('不正确'); this.nextElementSibling.className = 'error'; this.nextElementSibling.innerHTML = '<i class="error_icon"></i> 格式不正确,请从新输入 '; } } }; //密码验证:/^[a-zA-Z0-9_-]{6,16}$/ //再次输入密码只需匹配与上次输入的密码值 是否一致 3.5正则替换replacereplace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。var str = 'andy和red'; var newStr = str.replace('andy', 'baby'); console.log(newStr)//baby和red //等同于 此处的andy可以写在正则表达式内 var newStr2 = str.replace(/andy/, 'baby'); console.log(newStr2)//baby和red //全部替换 var str = 'abcabc' var nStr = str.replace(/a/,'哈哈') console.log(nStr) //哈哈bcabc //全部替换g var nStr = str.replace(/a/a,'哈哈') console.log(nStr) //哈哈bc哈哈bc //忽略大小写i var str = 'aAbcAba'; var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈" 案例:过滤敏感词汇<textarea name="" id="message"></textarea> <button>提交</button> <div></div> <script> var text = document.querySelector('textarea'); var btn = document.querySelector('button'); var div = document.querySelector('div'); btn.onclick = function() { div.innerHTML = text.value.replace(/激情|gay/g, '**'); } </script>
2022年07月10日
110 阅读
0 评论
0 点赞
2022-07-10
函数的定义和调用
1.1函数的定义方式 方式1 函数声明方式 function 关键字 (命名函数)function fn(){} 方式2 函数表达式(匿名函数)var fn = function(){} 方式3 new Function()var f = new Function('a', 'b', 'console.log(a + b)'); f(1, 2); var fn = new Function('参数1','参数2'..., '函数体') 注意 /*Function 里面参数都必须是字符串格式 第三种方式执行效率低,也不方便书写,因此较少使用 所有函数都是 Function 的实例(对象) 函数也属于对象 */ 1.2函数的调用/* 1. 普通函数 */ function fn() { console.log('人生的巅峰'); } fn(); /* 2. 对象的方法 */ var o = { sayHi: function() { console.log('人生的巅峰'); } } o.sayHi(); /* 3. 构造函数*/ function Star() {}; new Star(); /* 4. 绑定事件函数*/ btn.onclick = function() {}; // 点击了按钮就可以调用这个函数 /* 5. 定时器函数*/ setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次 /* 6. 立即执行函数(自调用函数)*/ (function() { console.log('人生的巅峰'); })(); 2.this2.1函数内部的this指向这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同一般指向我们的调用者.2.2改变函数内部 this 指向2.2.1 call方法call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向应用场景: 经常做继承.var o = { name: 'andy' } function fn(a, b) { console.log(this); console.log(a+b) }; fn(1,2)// 此时的this指向的是window 运行结果为3 fn.call(o,1,2)//此时的this指向的是对象o,参数使用逗号隔开,运行结果为3 以上代码运行结果为:2.2.2 apply方法apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。应用场景: 经常跟数组有关系var o = { name: 'andy' } function fn(a, b) { console.log(this); console.log(a+b) }; fn()// 此时的this指向的是window 运行结果为3 fn.apply(o,[1,2])//此时的this指向的是对象o,参数使用数组传递 运行结果为3 2.2.3 bind方法bind() 方法不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind应用场景:不调用函数,但是还想改变this指向 var o = { name: 'andy' }; function fn(a, b) { console.log(this); console.log(a + b); }; var f = fn.bind(o, 1, 2); //此处的f是bind返回的新函数 f();//调用新函数 this指向的是对象o 参数使用逗号隔开 2.2.4 call、apply、bind三者的异同 共同点 : 都可以改变this指向 不同点: call 和 apply 会调用函数, 并且改变函数内部this指向. call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递 bind 不会调用函数, 可以改变函数内部this指向. 应用场景 call 经常做继承. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值 bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向. 3.严格模式3.1什么是严格模式JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript变体的一种方式,即在严格的条件下运行 JS 代码。严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。严格模式对正常的 JavaScript 语义做了一些更改:1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。2.消除代码运行的一些不安全之处,保证代码运行的安全。3.提高编译器效率,增加运行速度。4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名3.2开启严格模式严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。 情况一 :为脚本开启严格模式 有的 script 脚本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他script 脚本文件。(function (){ //在当前的这个自调用函数中有开启严格模式,当前函数之外还是普通模式 "use strict"; var num = 10; function fn() {} })(); //或者 <script> "use strict"; //当前script标签开启了严格模式 </script> <script> //当前script标签未开启严格模式 </script> 情况二: 为函数开启严格模式 要给某个函数开启严格模式,需要把“use strict”; (或 'use strict'; ) 声明放在函数体所有语句之前。function fn(){ "use strict"; return "123"; } //当前fn函数开启了严格模式 3.3严格模式中的变化严格模式对 Javascript 的语法和行为,都做了一些改变。'use strict' num = 10 console.log(num)//严格模式后使用未声明的变量 -------------------------------------------------------------------------------- var num2 = 1; delete num2;//严格模式不允许删除变量 -------------------------------------------------------------------------------- function fn() { console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined } fn(); --------------------------------------------------------------------------------- function Star() { this.sex = '男'; } // Star();严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错. var ldh = new Star(); console.log(ldh.sex); ---------------------------------------------------------------------------------- setTimeout(function() { console.log(this); //严格模式下,定时器 this 还是指向 window }, 2000); 更多严格模式要求参考4.高阶函数高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。此时fn 就是一个高阶函数函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。同理函数也可以作为返回值传递回来5.闭包5.1变量的作用域复习变量根据作用域的不同分为两种:全局变量和局部变量。 函数内部可以使用全局变量。 函数外部不可以使用局部变量。 当函数执行完毕,本作用域内的局部变量会销毁。 5.2什么是闭包闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。5.3闭包的作用作用:延伸变量的作用范围。 function fn() { var num = 10; function fun() { console.log(num); } return fun; } var f = fn(); f(); 5.4闭包的案例 利用闭包的方式得到当前li 的索引号 for (var i = 0; i < lis.length; i++) { // 利用for循环创建了4个立即执行函数 // 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量 (function(i) { lis[i].onclick = function() { console.log(i); } })(i); } 闭包应用-3秒钟之后,打印所有li元素的内容 for (var i = 0; i < lis.length; i++) { (function(i) { setTimeout(function() { console.log(lis[i].innerHTML); }, 3000) })(i); } 闭包应用-计算打车价格 /*需求分析 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格 如果有拥堵情况,总价格多收取10块钱拥堵费*/ var car = (function() { var start = 13; // 起步价 局部变量 var total = 0; // 总价 局部变量 return { // 正常的总价 price: function(n) { if (n <= 3) { total = start; } else { total = start + (n - 3) * 5 } return total; }, // 拥堵之后的费用 yd: function(flag) { return flag ? total + 10 : total; } } })(); console.log(car.price(5)); // 23 console.log(car.yd(true)); // 33 5.5案例 var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { return function() { return this.name; }; } }; console.log(object.getNameFunc()()) ----------------------------------------------------------------------------------- var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { var that = this; return function() { return that.name; }; } }; console.log(object.getNameFunc()()) 6.递归6.1什么是递归**递归:**如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数**注意:**递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。6.2利用递归求1~n的阶乘//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n function fn(n) { if (n == 1) { //结束条件 return 1; } return n * fn(n - 1); } console.log(fn(3)); 6.3利用递归求斐波那契数列// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21... // 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值 // 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值 function fb(n) { if (n === 1 || n === 2) { return 1; } return fb(n - 1) + fb(n - 2); } console.log(fb(3)); 6.4利用递归遍历数据// 我们想要做输入id号,就可以返回的数据对象 var data = [{ id: 1, name: '家电', goods: [{ id: 11, gname: '冰箱', goods: [{ id: 111, gname: '海尔' }, { id: 112, gname: '美的' }, ] }, { id: 12, gname: '洗衣机' }] }, { id: 2, name: '服饰' }]; //1.利用 forEach 去遍历里面的每一个对象 function getID(json, id) { var o = {}; json.forEach(function(item) { // console.log(item); // 2个数组元素 if (item.id == id) { // console.log(item); o = item; return o; // 2. 我们想要得里层的数据 11 12 可以利用递归函数 // 里面应该有goods这个数组并且数组的长度不为 0 } else if (item.goods && item.goods.length > 0) { o = getID(item.goods, id); } }); return o; }
2022年07月10日
115 阅读
0 评论
0 点赞
2022-07-03
构造函数和原型
1.1对象的三种创建方式--复习 字面量方式var obj = {}; new关键字var obj = new Object(); 构造函数方式function Person(name,age){ this.name = name; this.age = age; } var obj = new Person('zs',12); 1.2静态成员和实例成员1.2.1实例成员实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问 function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我会唱歌'); } } var ldh = new Star('刘德华', 18); console.log(ldh.uname);//实例成员只能通过实例化的对象来访问 1.2.2静态成员静态成员 在构造函数本身上添加的成员 如下列代码中 sex 就是静态成员,静态成员只能通过构造函数来访问 function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我会唱歌'); } } Star.sex = '男'; var ldh = new Star('刘德华', 18); console.log(Star.sex);//静态成员只能通过构造函数来访问 1.3构造函数的问题构造函数方法很好用,但是存在浪费内存的问题。1.4构造函数原型prototype构造函数通过原型分配的函数是所有对象所共享的。JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。function Star(uname, age) { this.uname = uname; this.age = age; } Star.prototype.sing = function() { console.log('我会唱歌'); } var ldh = new Star('刘德华', 18); var zxy = new Star('张学友', 19); ldh.sing();//我会唱歌 zxy.sing();//我会唱歌 1.5对象原型对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。 __proto__对象原型和原型对象 prototype 是等价的 __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype 1.6constructor构造函数对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。 constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。 一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如: function Star(uname, age) { this.uname = uname; this.age = age; } // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数 Star.prototype = { // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 constructor: Star, // 手动设置指回原来的构造函数 sing: function() { console.log('我会唱歌'); }, movie: function() { console.log('我会演电影'); } } var zxy = new Star('张学友', 19); console.log(zxy) 以上代码运行结果,设置constructor属性如图:如果未设置constructor属性,如图:1.7原型链 每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。1.8构造函数实例和原型对象三角关系1.构造函数的prototype属性指向了构造函数原型对象 2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象 3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数 1.9原型链和成员的查找机制任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。 如果还没有就查找原型对象的原型(Object的原型对象)。 依此类推一直找到 Object 为止(null)。 __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。 1.10原型对象中this指向构造函数中的this和原型对象的this,都指向我们new出来的实例对象function Star(uname, age) { this.uname = uname; this.age = age; } var that; Star.prototype.sing = function() { console.log('我会唱歌'); that = this; } var ldh = new Star('刘德华', 18); // 1. 在构造函数中,里面this指向的是对象实例 ldh console.log(that === ldh);//true // 2.原型对象函数里面的this 指向的是 实例对象 ldh 1.11通过原型为数组扩展内置方法 Array.prototype.sum = function() { var sum = 0; for (var i = 0; i < this.length; i++) { sum += this[i]; } return sum; }; //此时数组对象中已经存在sum()方法了 可以始终 数组.sum()进行数据的求 2.继承2.1call() call()可以调用函数 call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3..使用逗号隔开连接 function fn(x, y) { console.log(this); console.log(x + y); } var o = { name: 'andy' }; fn.call(o, 1, 2);//调用了函数此时的this指向了对象o, 2.2子构造函数继承父构造函数中的属性 先定义一个父构造函数 再定义一个子构造函数 子构造函数继承父构造函数的属性(使用call方法) // 1. 父构造函数 function Father(uname, age) { // this 指向父构造函数的对象实例 this.uname = uname; this.age = age; } // 2 .子构造函数 function Son(uname, age, score) { // this 指向子构造函数的对象实例 3.使用call方式实现子继承父的属性 Father.call(this, uname, age); this.score = score; } var son = new Son('刘德华', 18, 100); console.log(son); 2.3借用原型对象继承方法 先定义一个父构造函数 再定义一个子构造函数 子构造函数继承父构造函数的属性(使用call方法) // 1. 父构造函数 function Father(uname, age) { // this 指向父构造函数的对象实例 this.uname = uname; this.age = age; } Father.prototype.money = function() { console.log(100000); }; // 2 .子构造函数 function Son(uname, age, score) { // this 指向子构造函数的对象实例 Father.call(this, uname, age); this.score = score; } // Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化 Son.prototype = new Father(); // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数 Son.prototype.constructor = Son; // 这个是子构造函数专门的方法 Son.prototype.exam = function() { console.log('孩子要考试'); } var son = new Son('刘德华', 18, 100); console.log(son); 如上代码结果如图:3.ES5新增方法3.1数组方法forEach遍历数组 arr.forEach(function(value, index, array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 }) //相当于数组遍历的 for循环 没有返回值 3.2数组方法filter过滤数组 var arr = [12, 66, 4, 88, 3, 7]; var newArr = arr.filter(function(value, index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value >= 20; }); console.log(newArr);//[66,88] //返回值是一个新数组 3.3数组方法somesome 查找数组中是否有满足条件的元素 var arr = [10, 30, 4]; var flag = arr.some(function(value,index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value < 3; }); console.log(flag);//false返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环 3.4筛选商品案例 定义数组对象数据var data = [{ id: 1, pname: '小米', price: 3999 }, { id: 2, pname: 'oppo', price: 999 }, { id: 3, pname: '荣耀', price: 1299 }, { id: 4, pname: '华为', price: 1999 }, ]; 使用forEach遍历数据并渲染到页面中data.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); }); 根据价格筛选数据 获取到搜索按钮并为其绑定点击事件search_price.addEventListener('click', function() { }); 使用filter将用户输入的价格信息筛选出来search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { //start.value是开始区间 //end.value是结束的区间 return value.price >= start.value && value.price <= end.value; }); console.log(newDate); }); 将筛选出来的数据重新渲染到表格中 将渲染数据的逻辑封装到一个函数中function setDate(mydata) { // 先清空原来tbody 里面的数据 tbody.innerHTML = ''; mydata.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); }); } 将筛选之后的数据重新渲染 search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { return value.price >= start.value && value.price <= end.value; }); console.log(newDate); // 把筛选完之后的对象渲染到页面中 setDate(newDate); }); 根据商品名称筛选 获取用户输入的商品名称 为查询按钮绑定点击事件,将输入的商品名称与这个数据进行筛选 search_pro.addEventListener('click', function() { var arr = []; data.some(function(value) { if (value.pname === product.value) { // console.log(value); arr.push(value); return true; // return 后面必须写true } }); // 把拿到的数据渲染到页面中 setDate(arr); }) 3.5some和forEach区别 如果查询数组中唯一的元素, 用some方法更合适,在some 里面 遇到 return true 就是终止遍历 迭代效率更高 在forEach 里面 return 不会终止迭代 3.6trim方法去除字符串两端的空格var str = ' hello ' console.log(str.trim()) //hello 去除两端空格 var str1 = ' he l l o ' console.log(str.trim()) //he l l o 去除两端空格 3.7获取对象的属性名Object.keys(对象) 获取到当前对象中的属性名 ,返回值是一个数组 var obj = { id: 1, pname: '小米', price: 1999, num: 2000 }; var result = Object.keys(obj) console.log(result)//[id,pname,price,num] 3.8Object.definePropertyObject.defineProperty设置或修改对象中的属性Object.defineProperty(对象,修改或新增的属性名,{ value:修改或新增的属性的值, writable:true/false,//如果值为false 不允许修改这个属性值 enumerable: false,//enumerable 如果值为false 则不允许遍历 configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性 })
2022年07月03日
92 阅读
0 评论
0 点赞
2022-07-02
JavaScript高级基础
1.面向过程与面向对象1.1面向过程 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。 1.2面向对象 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。 1.3面向过程与面向对象对比 面向过程 面向对象 优点 性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 缺点 不易维护、不易复用、不易扩展 性能比面向过程低 2.对象与类2.1对象对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物 属性:事物的特征,在对象中用属性来表示(常用名词) 方法:事物的行为,在对象中用方法来表示(常用动词) 2.1.1创建对象//以下代码是对对象的复习 //字面量创建对象 var ldh = { name: '刘德华', age: 18 } console.log(ldh); //构造函数创建对象 function Star(name, age) { this.name = name; this.age = age; } var ldh = new Star('刘德华', 18)//实例化对象 console.log(ldh); 如上两行代码运行结果为:2.2类 在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象 2.2.1创建类 语法: //步骤1 使用class关键字 class name { // class body } //步骤2使用定义的类创建实例 注意new关键字 var xx = new name(); 示例 // 1. 创建类 class 创建一个 明星类 class Star { // 类的共有属性放到 constructor 里面 constructor(name, age) { this.name = name; this.age = age; } } // 2. 利用类创建对象 new var ldh = new Star('刘德华', 18); console.log(ldh); 以上代码运行结果:通过结果我们可以看出,运行结果和使用构造函数方式一样2.2.2类创建添加属性和方法 // 1. 创建类 class 创建一个类 class Star { // 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数 constructor(uname, age) { this.uname = uname; this.age = age; }//------------------------------------------->注意,方法与方法之间不需要添加逗号 sing(song) { console.log(this.uname + '唱' + song); } } // 2. 利用类创建对象 new var ldh = new Star('刘德华', 18); console.log(ldh); // Star {uname: "刘德华", age: 18} ldh.sing('冰雨'); // 刘德华唱冰雨 以上代码运行结果:注意哟: 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象 constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数 多个函数方法之间不需要添加逗号分隔 生成实例 new 不能省略 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function 2.2.3类的继承 语法 // 父类 class Father{ } // 子类继承父类 class Son extends Father { } 示例 class Father { constructor(surname) { this.surname= surname; } say() { console.log('你的姓是' + this.surname); } } class Son extends Father{ // 这样子类就继承了父类的属性和方法 } var damao= new Son('刘'); damao.say(); //结果为 你的姓是刘 以上代码运行结果: 子类使用super关键字访问父类的方法//定义了父类 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } //子元素继承父类 class Son extends Father { constructor(x, y) { super(x, y); //使用super调用了父类中的构造函数 } } var son = new Son(1, 2); son.sum(); //结果为3 注意: 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则) 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用 // 父类有加法方法 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } // 子类继承父类加法方法 同时 扩展减法方法 class Son extends Father { constructor(x, y) { // 利用super 调用父类的构造函数 super 必须在子类this之前调用,放到this之后会报错 super(x, y); this.x = x; this.y = y; } subtract() { console.log(this.x - this.y); } } var son = new Son(5, 3); son.subtract(); //2 son.sum();//8 以上代码运行结果为: 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用. constructor中的this指向的是new出来的实例对象 自定义的方法,一般也指向的new出来的实例对象 绑定事件之后this指向的就是触发事件的事件源 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象 3.面向对象版tab 栏切换3.1功能需求 点击 tab栏,可以切换效果. 点击 + 号, 可以添加 tab 项和内容项. 点击 x 号, 可以删除当前的tab项和内容项. 双击tab项文字或者内容项文字可以修改里面的文字内容 3.2案例准备 获取到标题元素 获取到内容元素 获取到删除的小按钮 x号 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改) 时刻注意this的指向问题 3.3切换 为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引 this.lis[i].index = i; this.lis[i].onclick = this.toggleTab; 使用排他,实现只有一个元素的显示 toggleTab() { //将所有的标题与内容类样式全部移除 for (var i = 0; i < this.lis.length; i++) { this.lis[i].className = ''; this.sections[i].className = ''; } //为当前的标题添加激活样式 this.className = 'liactive'; //为当前的内容添加激活样式 that.sections[this.index].className = 'conactive'; } 3.4添加 为添加按钮+ 绑定点击事件 this.add.onclick = this.addTab; 实现标题与内容的添加,做好排他处理addTab() { that.clearClass(); // (1) 创建li元素和section元素 var random = Math.random(); var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"> </span></li>'; var section = '<section class="conactive">测试 ' + random + '</section>'; // (2) 把这两个元素追加到对应的父元素里面 that.ul.insertAdjacentHTML('beforeend', li); that.fsection.insertAdjacentHTML('beforeend', section); that.init(); } 3.5删除 为元素的删除按钮x绑定点击事件 this.remove[i].onclick = this.removeTab; 获取到点击的删除按钮的所在的父元素的所有,删除对应的标题与内容 removeTab(e) { e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件 var index = this.parentNode.index; console.log(index); // 根据索引号删除对应的li 和section remove()方法可以直接删除指定的元素 that.lis[index].remove(); that.sections[index].remove(); that.init(); // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变 if (document.querySelector('.liactive')) return; // 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态 index--; // 手动调用我们的点击事件 不需要鼠标触发 that.lis[index] && that.lis[index].click(); } 3.6编辑 为元素(标题与内容)绑定双击事件 this.spans[i].ondblclick = this.editTab; this.sections[i].ondblclick = this.editTab; 在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递editTab() { var str = this.innerHTML; // 双击禁止选定文字 window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); // alert(11); this.innerHTML = '<input type="text" />'; var input = this.children[0]; input.value = str; input.select(); // 文本框里面的文字处于选定状态 // 当我们离开文本框就把文本框里面的值给span input.onblur = function() { this.parentNode.innerHTML = this.value; }; // 按下回车也可以把文本框里面的值给span input.onkeyup = function(e) { if (e.keyCode === 13) { // 手动调用表单失去焦点事件 不需要鼠标离开操作 this.blur(); } } }
2022年07月02日
122 阅读
0 评论
1 点赞
2022-06-07
最详细的 Go学习笔记总结
第1章 Go介绍package main import "fmt" func main() { fmt.Println("hello word") } 第2章 Go基本语法2.1变量2.1.1. go语言中变量分为局部变量和全局变量 局部变量,是定义在打括号{}内部的变量,打括号内部也是局部变量的作用域 全局变量,是定义在函数和打括号外部{}的变量 2.1.2. 变量声明格式: var 变量名 变量类型 批量声明未初始化的变量var { a int b string c []float32 e struct { x int y string } } 初始化变量var a int = 20 #标准声明格式 var b = 30 #自动推断类型格式 c := 40 #初始化声明格式,首选 2.1.3.变量多重赋值以简单算法交换为例,传统写法如下var a int = 10 var b int = 20 b,a = a,b 2.1.4.匿名变量Go语言中的函数可以返回多个值,而事实上并不是所有返回值都用的上,那么就可以用匿名变量 “_” 替换即可,匿名变量不占用命名空间,也不会分配内存func GetData()(int,int){ return 10,20 } a,_ := GetData() //舍弃第二个返回值 _,b = GetData()//舍弃第一个返回值 2.2 数据类型2.3 打印格式化2.4 数据类型转换Go语言采用数据类型前置加括号的方式进行类型转换,格式如:T(表达式)。T表示要转换的类型a := 10 b := string(a) //将int型转换为string型 c := float32(a) //将int型转换为float型 2.5 常量相对于变量,常量是不变的值。 常量是一个简单的标识符,在程序运行时,不会被修改格式如下: const 标识符 [类型] = 值 const PAI string = "abc" 2.5.1 常量用于枚举 const ( USERNAME = "geinihua" PASSWORD = "geinihua" NETWORK = "tcp" SERVER = "10.247.22.146" PORT = "3306" DATABASE = "demo" ) dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME,PASSWORD,NETWORK,SERVER,PORT,DATABASE) 常量组中如果不指定类型和初始值,则与上一行非空常量值相同const ( a=10 b c ) fmt.PrintLn(a,b,c) //输出结果10 10 10 2.5.2 iota枚举 iota常量自动生成器,每隔一行,自动加1 iota给常量赋值使用 iota遇到下个const,会重置为0 多个常量可以写一个iota,在一个括号里 多重赋值,在同一行,值一样 3. 流程控制 3.1 if 条件判断语句 func max(num1, num2 int) int { /* 声明局部变量 */ var result int if num1 > num2 { result = num1 } else { result = num2 } return result } 3.2 switch 条件选择语句 grade := "" score := 88.5 switch true { case score >=90: grade = "A" case score >=80: grade = "B" case score >=70: grade = "C" default: grade="E" } fmt.Printf("你的登记是: %s\n",grade ) 3.3 for 循环语句 第一种写法: for i:=0;i<=20 ;i++ { fmt.Printf("%d\n",i) } 第二种写法: var i int for i<=20 { fmt.Printf("%d\n",i) } 第三种写法(for ...range): str := "123ABCabc好" for i,value := range str{ fmt.Printf("第 %d 位的ASCII值=%d,字符是%c\n",i,value,value) } 4.Go语言的函数与指针4.1 函数func(参数列表)(返回参数列表){ //函数体 } 4.1.3 函数变量函数变量是把函数作为值保存到变量中.在Golang中,,函数也是一种类型,可以和其他类型一样被保存在变量中type myFunc func(int) bool func main(){ nums:=[]int{10,20,40,16,17,3030,49849,204394,43943,2923,23923,} fmt.Println(filter(nums,isEven)) fmt.Println(filter(nums,isAdd)) } func filter(arr []int, f myFunc) []int { var result []int for _, value := range arr { if f(value) { result = append(result, value) } } return result } func isEven(num int) bool{ if num%2 == 0 { return true }else { return false } } func isAdd(num int) bool{ if num%2 == 0 { return false } return true } 4.1.4 匿名函数匿名函数没有函数名,只有函数体,可以作为一种类型赋值给变量。匿名函数经常被用于实现回调函数、闭包等1.在定义匿名函数的时候就可以直接使用 res1 := func (n1 int, n2 int) int { return n1 + n2 }(10, 30) //括号里的10,30 就相当于参数列表,分别对应n1和n2 fmt.Println("res1=",res1) 2.将匿名函数赋给一个变量 res1 := func (n1 int, n2 int) int { return n1 + n2 } res2 := res1(50,50) fmt.Println("res1=",res2) 3.匿名函数作为回调函数 func vist(list []float64,f func(float64)) { for _,value:=range list{ f(value) } } List := []float64{1,2,5,20,90} vist(List, func(v float64) { sqrt := math.Pow(v,2) fmt.Println(sqrt) }) 4.1.5 闭包//函数f返回了一个函数,返回的这个函数就是一个闭包。这个函数本身中没有定义变量I的,而是引用了它所在的环境(函数f)中的变量i. func f(i int) func() int { return func() int{ i++ return i } } a:=f(0) fmt.Println(a()) //0 fmt.Println(a()) //1 fmt.Println(a()) //2 fmt.Println(a()) //3 4.1.6 可变参数语法格式: func 函数名(参数名...类型)(返回值列表){} 该语法格式定义了一个接受任何数目、任何类型参数的函数。这里特殊语法是三个点"...",在一个变量后面加上三个点,表示从该处开始接受可变参数func Tsum(nums ...int) { fmt.Println(nums) total:=0 for _,val := range nums{ total+=val } fmt.Println( total) } 4.1.7 golang单元测试要开始一个单元测试,需要准备一个 go 源码文件,在命名文件时需要让文件必须以_test结尾单元测试源码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,例如: 格式如下: func TestXXX( t *testing.T ) func sum2(n1 int, args ...int) int { sum := n1 for i := 0; i < len(args); i++ { sum += args[i] } return sum } func TestAvaiableSum(t *testing.T) { res := sum2(1, 23, 34, 56) fmt.Println("res=", res) } 4.2指针指针式存储另一个变量的内存地址的变量。变量是一种使用方便的占位符。一个指针变量可以指向任何一个值的内存地址 在Go语言中使用地址符&来获取变量的地址,一个变量前使用&会返回该变量的内存地址total:=20 fmt.Println("total的内存地址",&total) 4.2.1 声明指针格式:var 指针变量 *指针类型 声明指针,*T是指针变量的类型,它指向T类型的值,*号用于指定变量是一个指针var ip *int //指向整型的指针 var fp *float32 //指向浮点型的指针 指针使用流程1.定义指针变量 2.为指针变量赋值 3.访问指针变量中指向地址的值 获取指针变量指向的变量值:在指针类型的变量前加上号。如atype Student struct { name string age int sex int8 } func TestZhiz(t *testing.T) { s1:=Student{"steven",32,2} s2:=Student{"Sunny",10,1} var a *Student=&s1 //&s1的内存地址 var b *Student=&s2 //&s2的内存地址 fmt.Printf("s1类型为%T,值为%v\n",s1,s1) fmt.Printf("s2类型为%T,值为%v\n",s2,s2) fmt.Printf("a类型为%T,值为%v\n",a,a) fmt.Printf("b类型为%T,值为%v\n",b,b) fmt.Printf("s1的值等于a指针\n") fmt.Printf("s2的值等于b指针\n") fmt.Printf("*a类型为%T,值为%v\n",*a,*a) fmt.Printf("*b类型为%T,值为%v\n",*b,*b) } 空指针 if(ptr != nil) //ptr不是空指针 if(ptr == nil)//ptr是空指针 4.2.2 使用指针1.通过指针修改变量的值//指针修改变量的值 a2:=32 b2:=&a2 fmt.Println("a2的值",a2) //a2的值 32 fmt.Println("b2地址",b2) //b2地址 0xc4200142d8 fmt.Println("b2的值",*b2) //b2的值 32 *b2++ fmt.Println("b2的值",*b2) //b2的值 33 2.使用指针作为函数的参数将基本数据类型的指针作为函数的参数,可以实现对传入数据的修改,这是因为指针作为函数的参数只是赋值了一个指针,指针指向的内存没有发生改变func main(){ orgi:=68 ptr:=&orgi change(ptr) fmt.Println("执行函数后orgi的值",orgi) //执行函数后orgi的值 20 } func change(p *int) { *p=20 } 4.2.3 指针数组//指针数组 //格式:var ptr [3]*string ptrArr:=[COUNT]string{"abc","ABC","123","8888"} i:=0 //定义指针数组 var ptrPoint [COUNT]*string fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[<nil> <nil> <nil> <nil>] //将数组中的每个元素地址赋值给指针数组 for i=0;i<COUNT;i++ { ptrPoint[i] = &ptrArr[i] } fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[0xc42000e800 0xc42000e810 0xc42000e820 0xc42000e830] //循环取指针数组中的值 for i=0;i<COUNT;i++ { fmt.Printf("a[%d]=%v \n",i, *ptrPoint[i]) //a[0]=abc //a[1]=ABC //a[2]=123 //a[3]=8888 } 4.2.4 指针的指针指向指针的指针变量声明格式如下:var ptr **int//使用两个*号 //指针的指针 var a2 int var ptr2 *int var pptr **int a2=1234 ptr2=&a2 fmt.Println("ptr地址",ptr2) pptr=&ptr fmt.Println("pptr地址",pptr) fmt.Printf("变量a2=%d\n",a2) fmt.Printf("指针变量ptr2=%d\n",*ptr2) fmt.Printf("指向指针的指针量pptr=%d\n",**pptr) //输出结果 /* ptr地址 0xc4200d4140 pptr地址 0xc4200ec000 变量a2=1234 指针变量ptr2=1234 指向指针的指针量pptr=20 */ 4.3 函数的参数传递4.3.1 值传递(传值)值传递是指在调用函数时将实际参数复制一份传递到函数中,不会影响原内容数据 4.3.2 引用传递(传引用)1.引用传递是在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改将影响原内容数据 2.Go中可以借助指针来实现引用传递。函数参数使用指针参数,传参的时候其实是复制一份指针参数,也就是复制了一份变量地址 3.函数的参数如果是指针,当调用函数时,虽然参数是按复制传递的,但此时仅仅只是复制一个指针,也就是一个内存地址,这样不会造成内存浪费、时间开销 函数传int类型的值与引用对比package main import "fmt" func main() { //函数传int类型的值与引用对比 a:=200 fmt.Printf("变量a的内存地址%p,值为:%v\n",&a,a) changeIntVal(a) fmt.Printf("changeIntVal函数调用后变量a的内存地址%p,值为:%v\n",&a,a) changeIntPtr(&a) fmt.Printf("changeIntPtr函数调用后变量a的内存地址%p,值为:%v\n",&a,a) /* 变量a的内存地址0xc420080008,值为:200 changeIntVal函数,传递的参数n的内存地址:0xc420080018,值为:200 changeIntVal函数调用后变量a的内存地址0xc420080008,值为:200 changeIntPtr函数,传递的参数n的内存地址:0xc42008a020,值为:0xc420080008 changeIntPtr函数调用后变量a的内存地址0xc420080008,值为:50 */ } func changeIntVal(n int) { fmt.Printf("changeIntVal函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) n=90 } func changeIntPtr(n *int) { fmt.Printf("changeIntPtr函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) *n=50 } 函数传slice类型的值与引用对比import "fmt" func main() { //函数传slice类型的值与引用对比 a:=[]int{1,2,3,4} fmt.Printf("变量a的内存地址%p,值为:%v\n",&a,a) changeSliceVal(a) fmt.Printf("changeSliceVal函数调用后变量a的内存地址%p,值为:%v\n",&a,a) changeSlicePtr(&a) fmt.Printf("changeSlicePtr函数调用后变量a的内存地址%p,值为:%v\n",&a,a) } func changeSliceVal(n []int) { fmt.Printf("changeSliceVal函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) n[0]=90 } func changeSlicePtr(n *[]int) { fmt.Printf("changeSlicePtr函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) (*n)[1]=50 } 5.3 map 5.3.1 map概念 Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。 Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的 Map是hash表的一个引用,类型写为:map[key]value,其中的key, value分别对应一种数据类型,如:map[string]string 要求所有的key的数据类型相同,所有value数据类型相同(注:key与value可以有不同的数据类型) 5.3.2 map语法 声明map第一种方法 mapVar := map[key类型]value类型 第二种方法 mapVar := make(map[key类型]value类型) map初始化和遍历 mapVar:=map[string]string{ "a":"t1", "b":"t2", "c":"t3", } //遍历map for key, value := range mapVar { fmt.Printf("key=%v value=%v\n",key,value) } //查看元素在集合中是否存在 if value,ok:=mapVar["aa"];ok { fmt.Println("存在value",value) }else { fmt.Println("不存在value") } 5.3.3 map是引用类型 第6章 Go常用内置包可参考官网 字符串遍历 str:="strings包:遍历带有中文的字符串" for _, value := range []rune(str) { fmt.Printf("%c\n",value) } json序列化和反序列化 map序列化//第1种map声明方式 M2:= map[int]string{ 2:"aa", 3:"bb", } //第2种map声明方式 M:=make(map[int]string) M[1]="aaa" M[2]="bbb" res2,error:=json.Marshal(M2) res,error:=json.Marshal(M) if error!=nil { fmt.Printf("解析错误") } fmt.Printf(string(res2)) fmt.Printf(string(res)) //返回结果 //{"2":"aa","3":"bb"}{"1":"aaa","2":"bbb"} 结构体序列化package main import ( "encoding/json" "fmt" ) type Stu struct { Name string `json:"name"` Age int HIgh bool sex string Class *Class `json:"class"` } type Class struct { Name string Grade int } func main() { //实例化一个数据结构,用于生成json字符串 cla := new(Class) cla.Name = "1班" cla.Grade = 3 stu := Stu{ "张三", 18, true, "男", cla,//指针变量 } //Marshal失败时err!=nil jsonStu, err := json.Marshal(stu) if err != nil { fmt.Println("生成json字符串错误") } //jsonStu是[]byte类型,转化成string类型便于查看 fmt.Println(string(jsonStu)) 从结果中可以看出 只要是可导出成员(变量首字母大写),都可以转成json。因成员变量sex是不可导出的,故无法转成json。 如果变量打上了json标签,如Name旁边的 json:"name" ,那么转化成的json key就用该标签“name”,否则取变量名作为key,如“Age”,“HIgh”。 bool类型也是可以直接转换为json的value值。Channel, complex 以及函数不能被编码json字符串。当然,循环的数据结构也不行,它会导致marshal陷入死循环。 指针变量,编码时自动转换为它所指向的值,如cla变量。 (当然,不传指针,Stu struct的成员Class如果换成Class struct类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。) //反序列化操作Unmarshal() var per_data Stu err2 := json.Unmarshal([]byte(jsonStu),&per_data) if err2 != nil { fmt.Printf("反序列化错误:%v\n", err) } fmt.Printf("personal_json反序列化=%v\n", per_data) fmt.Printf("per_data=%v\n", *per_data.Class) } 第7 章Go面向对象结构体 匿名结构体 和结构体匿名字段 匿名结构体就是没有名字的结构体,无须通过type关键字定义就可以直接使用。创建匿名结构体的时候,同时也要创建结构体对象 //匿名结构体 addr:=struct{ name string age int }{"slaiven",39} fmt.Println(addr) 匿名字段就是在结构体中的字段没有名字,只包含一个没有字段名的类型 如果字段没有名字,那么默认使用类型作为字段名,同一类型只能有一个匿名字段//匿名字段 user:=new(User) user.string="apce" user.int=84 fmt.Printf("名字%v,年龄%v",user.string,user.int) //名字apce,年龄84 结构体嵌套 将一个结构当作另一结构体的字段(属性),这种就是结构体嵌套,可以模拟以下两种关系. 聚合关系:一个类作为另一个类的属性,一定要采用有名字的结构体作为字段 继承关系:一个类作为另一个类的子类。子类与父类的关系。采用匿名字段的形式,匿名字段就该结构体的父类//聚合关系:一个类作为另一个类的属性 type Address struct { province,city string } type Person struct { name string age int address *Address } func TestMoudelStrings(t *testing.T) { //实例化Address结构体 addr:=Address{} addr.province="北京市" addr.city="丰台区" //实例化Person结构体 p:=Person{} p.name="Strven" p.age=28 p.address=&addr fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",p.address.city) //如果修改了Person对象的address数据,那么对Address对象会有影响么?肯定的 p.address.city="大兴区" fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",addr.city) //修改Address对象,是否会影响Persion对象数据?肯定的 addr.city="朝阳区" fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",addr.city) } //继承关系:一个类作为另一个类的子类。子类与父类的关系 type Address struct { province,city string } type Person struct { name string age int Address //匿名字段,Address是Person的父类 } func TestMoudelStrings(t *testing.T) { //实例化Address结构体 addr:=Address{} addr.province="北京" addr.city="丰台区" //实例化Person结构体 p:=Person{"strven",38,addr} fmt.Printf("姓名:%v 年龄:%v 省:%v 市:%v\n",p.name,p.age,p.Address.province,p.Address.city) //姓名:strven 年龄:38 省:北京 市:丰台区 } 方法 Go中同时有函数和方法,方法的本质是函数,但是与函数又不同 1.含义不同,函数是一段具有独立功能的代码,可以被反复多次调用,而方法是一个类的行为功能,只有该类的对象才能调用 2.方法有接受者而函数没有,Go语言的方法是一种作用域特定类型变量的函数,这种类型变量叫作接受者(receiver),接受者的概念类似于传统面向对象中的this或self关键字 3.方法可以重名(接受者不同),而函数不能重名,type Per struct { name string age int } func ( p Per ) getData() { fmt.Printf("名字:%v 年龄:%v",p.name,p.age) //名字:aaa 年龄:39 } func TestMethod(t *testing.T) { p1:=Per{"aaa",39} p1.getData() } 方法继承 方法是可以继承的,如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该匿名字段中的方法type Human struct { name, phone string age int } type Stu struct { Human school string } type Employee struct { Human company string } func TestMethod(t *testing.T) { s1:=Stu{Human{"dav","1850103930",7}," 洛阳一中"} s1.SayHi() } func (h *Human) SayHi() { fmt.Printf("我是%s,%d岁,电话%s\n",h.name,h.age,h.phone) } 方法重写 type Human struct { name, phone string age int } type Stu struct { Human school string } type Employee struct { Human company string } func TestMethod(t *testing.T) { s1:=Stu{Human{"dav","1850103930",7}, " 洛阳一中"} s2:=Employee{Human{"dav","1850*****",17},"航天飞机"} s1.SayHi() s2.SayHi() } func (h *Human) SayHi() { fmt.Printf("我是%s,%d岁,电话%s\n",h.name,h.age,h.phone) } func (h *Stu) SayHi() { fmt.Printf("我是%s,%d岁,电话%s,学校%s\n",h.name,h.age,h.phone,h.school) } func (h *Employee) SayHi() { fmt.Printf("我是%s,%d岁,电话%s,工作%s\n",h.name,h.age,h.phone,h.company) } 第9章 Go文件I/O操作9.1文件信息 FileInfo接口 func main() { file:="./layout.html" printMessat(file) } func printMessat(filePath string) { fileinfo,error:=os.Stat(filePath) if error !=nil { fmt.Println("文件打开错误",error.Error()) } fmt.Printf("文件名:%s\n",fileinfo.Name()) fmt.Printf("文件权限:%s\n",fileinfo.Mode()) fmt.Printf("是否为目录:%s\n",fileinfo.IsDir()) fmt.Printf("文件最后修改权限:%s\n",fileinfo.ModTime()) fmt.Printf("文件大小:%s\n",fileinfo.Size()) } 文件路径 file1:="/Users/u51/Documents/go_learn/first.go" file2:="./layout.html" fmt.Printf("是否是绝对路径%v\n",filepath.IsAbs(file1)) fmt.Printf("是否是绝对路径%v\n",filepath.IsAbs(file2)) fmt.Printf("获取文件绝对路径%v\n",filepath.Abs(file2)) fmt.Printf("获取文件绝对路径%v\n",filepath.Abs(file2)) 9.2 文件常规操作 创建目录 os.MKdir() 创建一级目录 os.MKdirAll() 创建多级目录 创建文件 os.create() 创建文件 删除文件 os.Remove() 删除文件或空目录 os.RemoveAll() 移除所有所有路径及包含的子节点,file,error:=os.Create("test1.csv") if error!=nil{ fmt.Printf("文件创建失败") } file.Write([]byte("aaa\n")) file.WriteString("bbbb") file.WriteString("文件") 打开和关闭文件 os.Open从文件开始读取数据,返回值n是实际读取的字节数,如果读到文件末尾n为0或err为io.EOFfile,error:=os.Open("./test1.csv") if error!=nil{ fmt.Printf("打开错误") }else { bs:=make([]byte,1024,1024) for{ n,err:=file.Read(bs) if n==0||err==io.EOF{ fmt.Printf("读取文件结束-----") break } fmt.Println(string(bs[:])) } } defer file.Close() 复制文件 func main() { srcFile:="./test.csv" destFile:="./test1.csv" total,err:=copyFiles(srcFile,destFile) if err!=nil{ fmt.Printf(err.Error()) }else { fmt.Println("复制ok",total) } } func copyFiles(srcfile,destfile string)(int64,error) { file1,err:=os.Open(srcfile) if err!=nil{ return 0, err } file2,err:=os.OpenFile(destfile,os.O_RDWR|os.O_CREATE,os.ModePerm) if err != nil{ return 0, err } defer file1.Close() defer file2.Close() return io.Copy(file2,file1) } 9.3 ioutil包 ioutil包核心函数 方法 作用 ReadFile() 读取文件中所有数据,返回读取的字节数组 WriteFile() 向指定文件写入数据,如果文件不存在,则创建文件,写入文件之前清空文件 ReadDir() 读取一个目录下的所有子文件及目录名称 TempDir() 在当前目录下,创建一个以指定字符串为名称前缀的临时文件夹,并返回文件夹路径 TempFile() 在当前目录下,创建一个以指定字符串为名称的前缀的文件,并以读写模式打开,返回os.File指针 data,error:=ioutil.ReadFile("./test1.csv") if error!=nil{ fmt.Printf("打开错误") }else { fmt.Println(string(data)) } bs:=[]byte("hello中的空间打开") error:=ioutil.WriteFile("./test1.csv",bs,077) if error!=nil{ fmt.Printf("写入文件异常") }else { fmt.Printf("写入文件成功") } 9.4 bufio包bufio实现了带缓冲的I/O操作,达到高效读写 bufio.Reader结构体 bufio.Reader 常用方法 方法 作用 func NewReader(rd io.Reader) *Reader 创建一个具有默认大小缓冲区、从r读取的*Reader func NewReaderSize(rd io.Reader, size int) *Reader 创建一个具有size大小缓冲区,从r读取的*Reader func (b *Reader) Discard(n int) (discarded int, err error) 丢弃n个byte数据 func (b *Reader) Read(p []byte) (n int, err error) 读取n个byte数据 func (b *Reader) Buffered() int 返回缓冲区中现有的可读取的字节数 func (b *Reader) Peek(n int) ([]byte, error) 获取当前缓冲区内接下来的n个byte数据,但不是移动指针 func (b *Reader) ReadByte() (byte, error) 读取1个字节 func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) 读取一行数据,\n分隔 func (b *Reader) ReadString(delim byte) (string, error) 读取1个字符串 func (b *Reader) ReadRune() (r rune, size int, err error) 读取1个utf-8字符 func (b *Reader) Reset(r io.Reader) 清空缓冲区 bufio.Writer结构体 第10章 并行编程并发与并行 并发:同一时间段内执行多个任务 并行:同一时刻执行多个任务 Go语言的并发通过goroutine实现,goroutine类似于线程,属于用户态线程,我们可以根据需要创建成千上万个goroutine并发工作。goroutine是由Go语言的运行时(runtime)调度完成,而线程是由操作系统调度完成。 Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是 Go 语言并发模式的重要实现基础。 goroutine goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU 在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴 Go语言中使用goroutine非常简单,只需要在被调用的函数前面加上go 关键字,这样就可以为一个函数创建一个goroutine(也就是创建了一个线程) 一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。启动单个goroutinepackage main func t1Goroutine() { println("第一个goroutine函数") } func main() { go t1Goroutine() println("main over....") } //输出结果,为什么没有打印函数t1Goroutine中的内容呢???? main over.... 原因: 在程序启动时,Go程序就会为main()函数创建一个默认的goroutine.当main()函数返回的时候该goroutine就结束了,所有在main()中启动的goroutine会一同结束,main函数所在的goroutine就像是权利的游戏中的夜王,其他的goroutine都是异鬼,夜王一死它转化的那些异鬼也就全部GG了。所以我们要想办法让main函数等一等hello函数,最简单粗暴的方式就是time.Sleep了。执行上面代码会发现,先打印main over....然后再打印goroutine的内容 第一个goroutine函数,首先为什么会先打印main over....是因为我们在创建新的goroutine的时候需要花费一些时间,而此时main函数所在的goroutine是继续执行的启动多个goroutinepackage main import ( "fmt" "sync" ) //wait group 用来等待一组goroutines的结束,在主Goroutine里声明,并且设置要等待的goroutine的个数,每个goroutine执行完成之后调用 Done,最后在主Goroutines 里Wait即可 var wg sync.WaitGroup //声明一个全局变量 func t2Goroutine(i int ) { defer wg.Done() fmt.Println("Go ", i) } func main() { for i:=0;i<10;i++{ wg.Add(1) go t2Goroutine(i) } wg.Wait() } //输出结果 Go 5 Go 1 Go 0 Go 7 Go 8 Go 2 Go 6 Go 9 Go 4 Go 3 多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。goroutine与线程可增长的栈OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这么大。所以在Go语言中一次创建十万左右的goroutine也是可以的。goroutine调度GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。 G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。 P管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。 M(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的; P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。P的个数是通过runtime.GOMAXPROCS设定(最大256),Go1.5版本之后默认为物理线程数。 在并发量大的时候会增加一些P和M,但不会太多,切换太频繁的话得不偿失。单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。 其一大特点是goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身goroutine的超轻量,以上种种保证了go调度方面的性能GOMAXPROCSGo运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码直接在main中声明runtime.GOMAXPROCS(1)channel通道如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。声明channel类型var by1 []int //声明一个int类型的切片 var ch1 chan int //声明一个存放int类型的channel 声明的通道后需要使用make函数初始化之后才能使用。 // 初始化channel ch2:=make(chan int) // 初始化带缓冲区的chan ch3:=make(chan string) channel操作通道有发送(send)、接收(receive)和关闭(close)三种操作。发送和接收都使用<-符号。 现在我们先使用以下语句定义一个通道: ch := make(chan int) 将一个值发送到通道中ch<- 10//把数据10发送到ch中 从一个通道中接收值 data:= <-ch// 从ch中接收的值赋值给变量data <-ch //从ch中接收值,忽略结果 我们通过调用内置的close函数来关闭通道。close(ch) 关于关闭通道需要注意的事情是,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。关闭后的通道有以下特点: 对一个关闭的通道再发送值就会导致panic。 对一个关闭的通道进行接收会一直获取值直到通道为空。 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。 关闭一个已经关闭的通道会导致panic。 无缓冲的通道无缓冲通道又称为阻塞的通道func main() { // 初始化channel ch1:=make(chan int) ch1 <- 10 fmt.Printf("发送成功") } //输出结果 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() D:/work/workspace/go_space/goruntine.go:8 +0x5f 为什么会出现deadlock错误呢?因为我们使用ch1 := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送。上面的代码会阻塞在ch <- 10这一行代码形成死锁,那如何解决这个问题呢?一种方法是启用一个goroutine去接收值,例如:package main import "fmt" func recvData(c chan int) { x:=<-c fmt.Println("接收到的数据",x) } func main() { // 初始化channel ch1:=make(chan int) go recvData(ch1) ch1 <- 10 fmt.Printf("发送成功") } //输出结果 接收到的数据 10 发送成功 无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道有缓冲的通道解决上面问题的方法还有一种,使用带有缓冲区的通道func main() { // 初始化channel ch1:=make(chan int ,2) ch1 <- 10 fmt.Printf("发送成功") } 从通道循环取值当向通道中发送完数据时,我们可以通过close函数来关闭通道。package main import ( "fmt" ) func main() { ch1:=make(chan int) ch2:=make(chan int) // 循环生成100个数,存到ch1中 go func() { for i:=0;i<10;i++{ ch1<-i } close(ch1) }() // 从ch1中接收值,*2后存到ch2 go func() { for{ data,ok:=<-ch1 //通道关闭后再取值ok=false if !ok{ break } ch2<-data*data } close(ch2) }() //在主goroutine中range ch2 for i:=range ch2{// 通道关闭后会退出for range循环 fmt.Println(i) } } //输出结果 0 1 4 9 16 25 36 49 64 81 从上面的例子中我们看到有两种方式在接收值的时候判断该通道是否被关闭,不过我们通常使用的是for range的方式。使用for range遍历通道,当通道被关闭的时候就会退出for range单向通道 chan<- int是一个只写单向通道(只能对其写入int类型值),可以对其执行发送操作但是不能执行接收操作; <-chan int是一个只读单向通道(只能从其读取int类型值),可以对其执行接收操作但是不能执行发送操作。 第11章 反射反射可以在程序编译期将变量的信息如字段名称、类型、结构体信息等整合到可执行文件中,这样就可以在程序运行期获取类型的反射信息Go程序在运行期使用reflect包访问程序的反射信息。在Go语言的反射机制中,任何接口值都由是一个具体类型和具体类型的值两部分组成的。 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。reflect.TypeOf() 获取任意值的 类型对象 func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v\n", v) } func main() { var a float32 = 3.14 reflectType(a) // type:float32 var b int64 = 100 reflectType(b) // type:int64 } type name和type kind 在反射中关于类型还划分为两种:类型(Type)和种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空。type person struct { Name string `json:"name"` Age int `json:"age"` } func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v kind:%v\n", v.Name(), v.Kind()) } func main() { var a float32 = 3.14 reflectType(a) // type:float32 var b int64 = 100 reflectType(b) // type:int64 p:=person{ "struc", 20, } reflectType(p) //type:person kind:struct m:=make(map[int]string) reflectType(m) //type: kind:map var s []string reflectType(s) //type: kind:slice } reflect.ValueOf()reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。 func reValueOf(x interface{}) { v:=reflect.ValueOf(x) k:=v.Kind() switch k { case reflect.Int64: // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换 fmt.Printf("type is int64, value is %d\n", int64(v.Int())) case reflect.Int32: fmt.Printf("type is int32, value is %d\n", int32(v.Int())) } } func main() { var b int64 = 100 reValueOf(b) // type is int64, value is 100 } 通过反射设置变量值 想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值func relectSetVal(x interface{}) { v:=reflect.ValueOf(x) //反射中使用Elem()方法获取指针对应的值 if v.Elem().Kind()==reflect.Int64{ v.Elem().SetInt(200) } } func main() { var b int64 = 100 relectSetVal(&b) fmt.Println(b) //200 } 结构体反射 任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()和Field()方法获得结构体成员的详细信息。 type person struct { Name string `json:"name"` Age int `json:"age"` } func main() { p:=person{"test",20} r:=reflect.TypeOf(p) fmt.Println(r.NumField(),r.Name(),r.Kind()) //2 person struct //判断kind是struct if r.Kind()==reflect.Struct{ // 通过for循环遍历结构体的所有字段信息 for i:=0;i<r.NumField();i++{ field := r.Field(i) fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json")) } } // 通过字段名获取指定结构体字段信息 if name_field,ok:=r.FieldByName("Name");ok{ fmt.Printf("name:%s index:%d type:%v json tag:%v\n", name_field.Name, name_field.Index, name_field.Type, name_field.Tag.Get("json")) } } 第12章 worker pool(goroutine池)第13章 网络编程get请求func main() { resp, err := http.Get("http://www.baidu.com/") if err != nil { fmt.Printf("get failed, err:%v\n", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("read from resp.Body failed, err:%v\n", err) return } fmt.Println(string(body)) } 带参数的get请求关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。func httpGet2(requestUrl string) (err error) { Url, err := url.Parse(requestUrl) if err != nil { fmt.Printf("requestUrl parse failed, err:[%s]", err.Error()) return } params := url.Values{} params.Set("username","googlesearch") params.Set("passwd","golang") Url.RawQuery = params.Encode() requestUrl = Url.String() fmt.Printf("requestUrl:[%s]\n", requestUrl) resp, err := http.Get(requestUrl) if err != nil { fmt.Printf("get request failed, err:[%s]", err.Error()) return } defer resp.Body.Close() bodyContent, err := ioutil.ReadAll(resp.Body) fmt.Printf("resp status code:[%d]\n", resp.StatusCode) fmt.Printf("resp body data:[%s]\n", string(bodyContent)) return } 带参数post请求func httpPost(requestUrl string) (err error) { data := url.Values{} data.Add("username", "seemmo") data.Add("passwd", "da123qwe") resp, err := http.PostForm(requestUrl, data) if err != nil { fmt.Printf("get request failed, err:[%s]", err.Error()) return } defer resp.Body.Close() bodyContent, err := ioutil.ReadAll(resp.Body) fmt.Printf("resp status code:[%d]\n", resp.StatusCode) fmt.Printf("resp body data:[%s]\n", string(bodyContent)) return } TCP连接过程TCP服务端程序的处理流程: 1.监听端口 2.接收客户端请求建立连接 3.创建goroutine处理连接 TCP客户端程序的处理流程: 1.建立与服务端的链接 2.进行数据收发 3.关闭链接 服务端代码 package main import ( "bufio" "fmt" "net" ) //实现TCP通信步骤: //1.监听端口 //2.接收客户端请求建立连接 //3.创建goroutine处理连接 func process(conn net.Conn) { defer conn.Close() //延迟关闭 reader:=bufio.NewReader(conn) var b [128]byte for { n,err:=reader.Read(b[:]) if err!=nil{ fmt.Println("read from client failed, err:", err) break } recvStr := string(b[:n]) fmt.Println("收到client端发来的数据:", recvStr) conn.Write([]byte(recvStr)) // 发送数据 } } func main() { listen,err:=net.Listen("tcp","127.0.0.1:9090") if err!=nil{ fmt.Println("listen failed, err:", err) return } for{ conn,err:=listen.Accept() if err!=nil { fmt.Println("accept failed, err", err) continue } go process(conn) } } 客户端代码package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { conn,err:=net.Dial("tcp","127.0.0.1:9090") if err!=nil{ fmt.Println("err:",err) return } defer conn.Close() fmt.Println("请输入数据") inputReder:=bufio.NewReader(os.Stdin) for{ input,_:=inputReder.ReadString('\n') inputInfo := strings.Trim(input, "\r\n") if strings.ToUpper(inputInfo) == "Q" { // 如果输入q就退出 return } _, err = conn.Write([]byte(inputInfo)) // 发送数据 if err != nil { return } buf := [512]byte{} n, err := conn.Read(buf[:]) if err != nil { fmt.Println("recv failed, err:", err) return } fmt.Println(string(buf[:n])) } } 第14章 数据库操作下载依赖go get -u github.com/go-sql-driver/mysql 创建dbconf包package dbconf import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) // 定义一个全局对象db var db *sql.DB func InitDb() (err error) { dsn := "epai:epai@tcp(10.10.10.48:3306)/bind_dns?charset=utf8mb4&parseTime=True" // 不会校验账号密码是否正确 // 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db db,err = sql.Open("mysql", dsn) if err != nil { return } // 尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { return } return } type binddns struct { zone string data string } func QueryRowDemo() { var dns binddns sqlStr := "SELECT zone, data from dns_records" rows,err:=db.Query(sqlStr) defer rows.Close() if err !=nil { fmt.Printf("query failed, err:%v\n", err) } for rows.Next() { err:=rows.Scan(&dns.zone,&dns.data) if err != nil{ fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("域名:%s 记录值:%s\n", dns.zone,dns.data) } } func InserDemo() { sqlstr := "INSERT INTO `dns_records` VALUES (9, 'abc.com', '*', 'A', '10.10.10.83', 60, NULL, 'any', 255, 28800, 14400, 86400, 86400, 2015050917, 'ddns.net', 'ns.ddns.net.');" res, error := db.Exec(sqlstr) if error != nil { fmt.Printf("insert failed, err:%v\n", error) return } theID, err := res.LastInsertId() // 新插入数据的id if err != nil { fmt.Printf("get lastinsert ID failed, err:%v\n", err) return } fmt.Printf("insert success, the id is %d.\n", theID) } func UpdateDemo() { sqlstr:="update dns_records set data=? where id = ?" res,err:=db.Exec(sqlstr,"10.10.10.98",9) if err !=nil{ fmt.Printf("update failed, err:%v\n", err) return } res_row,err:=res.RowsAffected() if err !=nil{ fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("update success, affected rows:%d\n", res_row) } 在main.go引入dbconf包package main import ( "fmt" "test_go_mod2/dbconf" ) func main() { err:=dbconf.InitDb() if err != nil { fmt.Printf("init db failed,err:%v\n", err) return }else { fmt.Println("连接mysql成功") } dbconf.QueryRowDemo() dbconf.InserDemo() dbconf.UpdateDemo() } 预处理为什么预处理?优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。 避免SQL注入问题。 Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。//预查询 func PreareDemo() { sqlStr := "SELECT zone, data from dns_records where id < ?" stmt,err:=db.Prepare(sqlStr) if err !=nil { fmt.Printf("preare query failed, err:%v\n", err) } rows,err:=stmt.Query(8) defer rows.Close() if err !=nil { fmt.Printf("query failed, err:%v\n", err) } for rows.Next() { err:=rows.Scan(&dns.zone,&dns.data) if err != nil{ fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("域名:%s 记录值:%s\n", dns.zone,dns.data) } } 15章 gorm操作作者:yum玩坏了链接:https://juejin.cn/post/6903353632687652872来源:稀土掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2022年06月07日
166 阅读
0 评论
0 点赞
2022-06-05
可视化项目
01-项目介绍 应对现在数据可视化的趋势,越来越多企业需要在很多场景(营销数据,生产数据,用户数据)下使用,可视化图表来展示体现数据,让数据更加直观,数据特点更加突出。我们引入 '立可得' 数据可视化项目。 该项目除了使用了基础的DIV+CSS布局,还引入了一些C3技术,还引入了各类图表的绘制,以及高级的地图数据可视化案例。主要功能有:饼状图、柱状图、线形图、地图 ...02-使用技术完成该项目需要具备以下知识: div + css 布局 flex 布局 css3动画 css3渐变 css3边框图片 原生js + jquery 使用 rem适配 echarts基础 粗略代码统计: css 580行 html 450行 js 400行 (有效) 03-Echarts-介绍ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。大白话: 是一个JS插件 性能好可流畅运行PC与移动设备 兼容主流浏览器 提供很多常用图表,且可定制。 折线图、柱状图、散点图、饼图、K线图 04-Echarts-体验官方教程:[五分钟上手ECharts](https://www.echartsjs.com/zh/tutorial.html#5 分钟上手 ECharts)自己步骤: 下载echarts https://github.com/apache/incubator-echarts/tree/4.5.0 引入echarts dist/echarts.min.js 准备一个具备大小的DOM容器 <div id="main" style="width: 600px;height:400px;"></div> 初始化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); 05-Echarts-基础配置需要了解的主要配置:series xAxis yAxis grid tooltip title legend color series 系列列表。每个系列通过 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] } ] }; 06-REM适配 设计稿是1920px PC端适配: 宽度在 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; } } 07-基础布局html结构:<body> <div class="viewport"> <div class="column"> <!--概览--> <div></div> <!--监控--> <div></div> <!--点位--> <div></div> </div> <div class="column"> <!--地图--> <div></div> <!--用户--> <div></div> </div> <div class="column"> <!--订单--> <div></div> <!--销售--> <div></div> <div> <!--渠道--> <div></div> <!--季度--> <div></div> </div> <!--排行--> <div></div> </div> </div> </body> 效果图: 1920px * 1078px body 设置背景图 ,行高1.15 viewport 主体容器,限制最小宽度1024px,与最大宽度1920px,最小高度780px。 需要居中显示 使用logo.png做为背景图,在容器内显示 内间距 88px 20px 0 column 列容器,分三列,占比 3:4:3 中间容器外间距 32px 20px 0 css样式:/* 基础布局 */ 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; } 08-边框图片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 环绕,是完整的使用切割后的图片进行平铺。 DEMO代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>边框图片</title> <style> ul{ margin: 0; padding: 0; list-style: none; } li{ width: 350px; height: 160px; border: 20px solid #ccc; margin-top: 20px; } li:nth-child(1){ /*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; } li:nth-child(2){ /*平铺 从边框的中心向两侧开始平铺 会出现不完整的图片*/ border-image: url("images/border.jpg") 167/20px repeat; } li:nth-child(3){ /*默认的平铺方式*/ border-image: url("images/border.jpg") 167/20px stretch; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> </ul> </body> </html> 09-公用面板样式所有的面板的基础样式是一致的,提前布局好。切割示例图: 面板 .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; } 10-概览区域(overview)-布局html结构: <div class="overview panel"> <div class="inner"> <ul> <li> <h4>2,190</h4> <span> <i class="icon-dot" style="color: #006cff"></i> 设备总数 </span> </li> <li class="item"> <h4>190</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 季度新增 </span> </li> <li> <h4>3,001</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 运营设备 </span> </li> <li> <h4>108</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 异常设备 </span> </li> </ul> </div> </div> 样式描述: 容器高度 110px h4字体 28px #fff 左边距 4.8px 下间隙 8px span字体 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; } 11-监控区域(monitor)-布局html结构: <!--监控--> <div class="monitor panel"> <div class="inner"> <div class="tabs"> <a href="javascript:;" class="active">故障设备监控</a> <a href="javascript:;" >异常设备监控</a> </div> <div class="content"> <div class="head"> <span class="col">故障时间</span> <span class="col">设备地址</span> <span class="col">异常代码</span> </div> <div class="marquee-view"> <div class="marquee"> <div class="row"> <span class="col">20180701</span> <span class="col">11北京市昌平西路金燕龙写字楼</span> <span class="col">1000001</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190601</span> <span class="col">北京市昌平区城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190704</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000003</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20180701</span> <span class="col">北京市昌平区建路金燕龙写字楼</span> <span class="col">1000004</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000005</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000006</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建西路金燕龙写字楼</span> <span class="col">1000007</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000008</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000009</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190710</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000010</span> <span class="icon-dot"></span> </div> </div> </div> </div> <div class="content"> <div class="head"> <span class="col">异常时间</span> <span class="col">设备地址</span> <span class="col">异常代码</span> </div> <div class="marquee-view"> <div class="marquee"> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000001</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190703</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190704</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190705</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190706</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190707</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190708</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190709</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190710</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> </div> </div> </div> </div> </div> 监控区域 monitor 大盒子的高度是 480px结构解释: .tabs 标签选项 加上active激活选项 默认激活第一个选项 .content 切换内容 加上style="display: block;"显示内容 默认激活第一个内容 样式描述: .inner 容器内间距 24px 0 .tabs 容器内间距 0 36px a 容器 颜色: #1950c4 内间距:0 27px 字体:18px 第一个a容器 去除左侧内间距 加上右侧2px宽度边框#00f2f1 激活的时候 颜色白色 .content容器 占满剩余高度 flex:1 默认隐藏 .head 容器 行高 1.05 背景 rgba(255, 255, 255, 0.1) 内间距 12px 36px 颜色 #68d8fe 字体大小 14px row 容器 行高 1.05 内间距 12px 36px 颜色 #68d8ff 字体大小 12px .icon-dot 字体图标 绝对定位 左边0.2rem 透明度0 鼠标经过后:背景 rgba(255, 255, 255, 0.1) 透明度1 col容器 宽度:1rem 2.5rem 1rem 第二个col 一行不换行 溢出 省略 /* 监控区域 */ .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; } 12-监控区域-效果切换功能: 绑定 标签页点击 事件 当前容器加active其他容器移除active 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(); }); 动画功能: 实现思路: 先克隆列表,追加在后面 marquee-view 占满剩余高度,溢出隐藏 绝对定位,top 1.6rem bottom 0 宽度100%,溢出隐藏 使用animation实现动画 使用 translateY 向上位移 50% 动画时间15s,匀速播放,循环执行。 js代码: // 动画 $(".marquee-view .marquee").each(function() { // console.log($(this)); var rows = $(this).children().clone(); $(this).append(rows); }); css代码:/* 通过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; } 13-点位区域(point)-布局html结构: <!-- 点位 --> <div class="point panel"> <div class="inner"> <h3>点位分布统计</h3> <div class="chart"> <div class="pie"></div> <div class="data"> <div class="item"> <h4>320,11</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 点位总数 </span> </div> <div class="item"> <h4>418</h4> <span> <i class="icon-dot" style="color: #eacf19"></i> 本月新增 </span> </div> </div> </div> </div> </div> css样式: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; } 14-点位区域-饼图ECharts图表实现步骤: 从官方示例中找到类似图表,适当修改,引入到HTML页面中。 按照产品需求,来定制图表。 1. HTML引入图表// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} <br/>{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); })(); 2. 定制需求第一步:参考官方例子,熟悉里面参数具体含义option = { // 提示框组件 tooltip: { // trigger 触发方式。 非轴图形,使用item的意思是放到数据对应图形上触发提示 trigger: 'item', // 格式化提示内容: // a 代表series系列图表名称 // b 代表series数据名称 data 里面的name // c 代表series数据值 data 里面的value // d代表 当前数据/总数据的比例 formatter: "{a} <br/>{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(); }); 点位统计完整JavaScript代码:// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} <br/>{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(); }); })(); 15-地图区域 (map) -预留布局html结构: <!-- 地图 --> <div class="map"> <h3> <span class="icon-cube"></span> 设备数据统计 </h3> <div class="chart"> <div class="geo"></div> </div> </div> css样式:/* 地图 */ .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; } 16-用户统计 (users) -布局html结构: <!-- 用户 --> <div class="users panel"> <div class="inner"> <h3>全国用户总量统计</h3> <div class="chart"> <div class="bar"></div> <div class="data"> <div class="item"> <h4>120,899</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 用户总量 </span> </div> <div class="item"> <h4>248</h4> <span> <i class="icon-dot" style="color: #eacf19"></i> 本月新增 </span> </div> </div> </div> </div> </div> css样式:/* 用户模块 */ .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; } 17-用户统计 (users) -柱状图实现步骤: 从官方示例中找到最接近项目需求的例子,适当修改, 引入到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: 修改柱子的颜色 // 修改线性渐变色方式 1 color: new echarts.graphic.LinearGradient( // (x1,y2) 点到点 (x2,y2) 之间进行渐变 0, 0, 0, 1, [ { offset: 0, color: '#00fffb' }, // 0 起始颜色 { offset: 1, color: '#0061ce' } // 1 结束颜色 ] ), // 修改线性渐变色方式 2 color: { 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:调整数据,与省略图形定制 // series data: [2100,1900,1700,1560,1400,1200,1200,1200,900,750,600,480,240] // xAxis data: ['上海', '广州', '北京', '深圳', '合肥', '', '......', '', '杭州', '厦门', '济南', '成都', '重庆'] 省略图形 经过图形才显示提示,且省略的柱子不需要提示 图形单独设置颜色 // 中间省略的数据 准备三项 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(); }); 18-订单区域(order)-布局html结构: <!-- 订单 --> <div class="order panel"> <div class="inner"> <!-- 筛选 --> <div class="filter"> <a href="javascript:;" class="active">365天</a> <a href="javascript:;" >90天</a> <a href="javascript:;" >30天</a> <a href="javascript:;" >24小时</a> </div> <!-- 数据 --> <div class="data"> <div class="item"> <h4>20,301,987</h4> <span> <i class="icon-dot" style="color: #ed3f35;"></i> 订单量 </span> </div> <div class="item"> <h4>99834</h4> <span> <i class="icon-dot" style="color: #eacf19;"></i> 销售额(万元) </span> </div> </div> </div> </div> css样式:/* 订单 */ .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; } 18-订单区域(order)-效果 ( 课后作业 )实现步骤: 提前准备数据 点击后切tab激活样式 点击后切换数据内容 开启定时器动态切换数据 // 订单功能 (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) })(); 19-销售统计( sales )-布局html结构: <!-- 销售额 --> <div class="sales panel"> <div class="inner"> <div class="caption"> <h3>销售额统计</h3> <a href="javascript:;" class="active" >年</a> <a href="javascript:;" >季</a> <a href="javascript:;" >月</a> <a href="javascript:;" >周</a> </div> <div class="chart"> <div class="label">单位:万</div> <div class="line"></div> </div> </div> </div> css样式:/* 销售区域 */ .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%; } 19-销售统计( sales )-线形图实现步骤: 寻找官方的类似示例,给予分析, 引入到HTML页面中 按照需求来定制它。 **第一步:**寻找官方的类似示例,给予分析。官方参考示例:https://www.echartsjs.com/examples/zh/editor.html?c=line-stack// 销售统计模块 (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轴分割线) 轴两端是不需要内间距 boundaryGap xAxis: { type: 'category', data: ["周一", "周二"], axisTick: { show: false // 去除刻度线 }, axisLabel: { color: '#4c9bfd' // 文本颜色 }, axisLine: { show: false // 去除轴线 }, boundaryGap: false // 去除轴内间距 }, 需求4: y轴的定制 刻度去除 字体颜色:#4c9bfd 分割线颜色:#012f4a yAxis: { type: 'value', axisTick: { show: false // 去除刻度 }, axisLabel: { color: '#4c9bfd' // 文字颜色 }, splitLine: { lineStyle: { color: '#012f4a' // 分割线颜色 } } }, 需求5: 两条线形图定制 颜色分别:#00f2f1 #ed3f35 把折线修饰为圆滑 series 数据中添加 smooth 为 true color: ['#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 } }] 总结:现在给的是年份数据,还需要切换效果。19-销售统计( sales )-切换效果实现步骤: 准备切换需要依赖的数据 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) }) 第三步:tab栏自动切换效果 开启定时器每隔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(); }); 20-渠道区域&销售进度-布局html结构: <!-- 渠道 季度 --> <div class="wrap"> <div class="channel panel"> <div class="inner"> <h3>渠道分布</h3> <div class="data"> <div class="radar"></div> </div> </div> </div> <div class="quarter panel"> <div class="inner"> <h3>一季度销售进度</h3> <div class="chart"> <div class="box"> <div class="gauge"></div> <div class="label">75<small> %</small></div> </div> <div class="data"> <div class="item"> <h4>1,321</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 销售额(万元) </span> </div> <div class="item"> <h4>150%</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 同比增长 </span> </div> </div> </div> </div> </div> </div> css样式:/* 渠道区块 */ .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; } 21-渠道分布(channel)-雷达图实现步骤: 寻找官方的类似示例,给予分析,并引入到HTML页面中 按照需求来定制它 第一步: 参考类似实例: https://www.echartsjs.com/examples/zh/editor.html?c=radar-aqi(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(); }); })(); 22-销售进度 (quarter) -饼状图实现步骤: 寻找官方的类似示例,给予分析,引入到HTML页面中 按照需求来定制它。 第一步:参考官方示例:https://www.echartsjs.com/examples/zh/editor.html?c=pie-doughnut// 销售模块 饼形图 半圆形 设置方式 (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 23-热销排行(top)-布局html结构: <!-- 排行榜 --> <div class="top panel"> <div class="inner"> <div class="all"> <h3>全国热榜</h3> <ul> <li> <i class="icon-cup1" style="color: #d93f36;"></i> 可爱多 </li> <li> <i class="icon-cup2" style="color: #68d8fe;"></i> 娃哈啥 </li> <li> <i class="icon-cup3" style="color: #4c9bfd;"></i> 喜之郎 </li> </ul> </div> <div class="province"> <h3>各省热销 <i class="date">// 近30日 //</i></h3> <div class="data"> <ul class="sup"> <li> <span>北京</span> <span>25,179 <s class="icon-up"></s></span> </li> <li> <span>河北</span> <span>23,252 <s class="icon-down"></s></span> </li> <li> <span>上海</span> <span>20,760 <s class="icon-up"></s></span> </li> <li> <span>江苏</span> <span>23,252 <s class="icon-down"></s></span> </li> <li> <span>山东</span> <span>20,760 <s class="icon-up"></s></span> </li> </ul> <ul class="sub"> <!-- <li><span></span><span> <s class="icon-up"></s></span></li> --> </ul> </div> </div> </div> </div> css样式:/* 排行榜 */ .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); } } 23-热销排行(top)-效果实现思路: 准备后台返回的真实数据 利用数据渲染各省热销模块 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(`<span>${star.name}</span><span>${star.age}</span>`); 开始实现:第一步:得到后台数据(实际开发中,这个数据通过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 += `<li><span>${item.city}</span><span> ${item.sales} <s class= ${item.flag ? "icon-up" : "icon-down"}></s></span></li>`; }); $(".sup").html(supHTML); 第三步:当数据进入 tab 的时候 激活当前的tab样式,删除其他tab的样式 渲染各省热销 sub 模块 注意鼠标进入tab, 只遍历 当前索引号对应的 城市对象里面的 brands 拼接html格式字符串,进行渲染 第四步:默认激活第一个tab // 所有的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); } ); 24-Echarts-社区介绍社区就是一些,活跃的echart使用者,交流和贡献定制好的图表的地方。 在这里可以找到一些基于echart的高度定制好的图表,相当于基于jquery开发的插件,这里是基于echarts开发的第三方的图表。 25-Echarts-map使用(扩展)参考社区的例子: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' } } }, 总结:这例子是扩展案例,大家以后可以多看看社区里面的案例。26-总结总结:实现一个需求,需要去推导,具备推导的能力需要练习,时间问题。
2022年06月05日
111 阅读
0 评论
0 点赞
2022-06-02
jQuery事件操作
1.1. jQuery 事件注册 jQuery 为我们提供了方便的事件注册机制,是开发人员抑郁操作优缺点如下: 优点: 操作简单,且不用担心事件覆盖等问题。 缺点: 普通的事件注册不能做事件委托,且无法实现事件解绑,需要借助其他方法。 语法演示代码<body> <div></div> <script> $(function() { // 1. 单个事件注册 $("div").click(function() { $(this).css("background", "purple"); }); $("div").mouseenter(function() { $(this).css("background", "skyblue"); }); }) </script> </body> 1.2. jQuery 事件处理 因为普通注册事件方法的不足,jQuery又开发了多个处理方法,重点讲解如下: on(): 用于事件绑定,目前最好用的事件绑定方法 off(): 事件解绑 trigger() / triggerHandler(): 事件触发 1.2.1 事件处理 on() 绑定事件 因为普通注册事件方法的不足,jQuery又创建了多个新的事件绑定方法bind() / live() / delegate() / on()等,其中最好用的是: on()语法演示代码<body> <div></div> <ul> <li>我们都是好孩子</li> <li>我们都是好孩子</li> <li>我们都是好孩子</li> </ul> <ol></ol> <script> $(function() { // (1) on可以绑定1个或者多个事件处理程序 // $("div").on({ // mouseenter: function() { // $(this).css("background", "skyblue"); // }, // click: function() { // $(this).css("background", "purple"); // } // }); $("div").on("mouseenter mouseleave", function() { $(this).toggleClass("current"); }); // (2) on可以实现事件委托(委派) // click 是绑定在ul 身上的,但是 触发的对象是 ul 里面的小li // $("ul li").click(); $("ul").on("click", "li", function() { alert(11); }); // (3) on可以给未来动态创建的元素绑定事件 $("ol").on("click", "li", function() { alert(11); }) var li = $("<li>我是后来创建的</li>"); $("ol").append(li); }) </script> </body> 1.2.2. 案例:发布微博案例1.点击发布按钮, 动态创建一个小li,放入文本框的内容和删除按钮, 并且添加到ul 中。2.点击的删除按钮,可以删除当前的微博留言。 代码实现略。(详情参考源代码)1.2.3. 事件处理 off() 解绑事件 当某个事件上面的逻辑,在特定需求下不需要的时候,可以把该事件上的逻辑移除,这个过程我们称为事件解绑。jQuery 为我们提供 了多种事件解绑方法:die() / undelegate() / off() 等,甚至还有只触发一次的事件绑定方法 one(),在这里我们重点讲解一下 off() ;语法演示代码<body> <div></div> <ul> <li>我们都是好孩子</li> <li>我们都是好孩子</li> <li>我们都是好孩子</li> </ul> <p>我是一个P标签</p> <script> $(function() { // 事件绑定 $("div").on({ click: function() { console.log("我点击了"); }, mouseover: function() { console.log('我鼠标经过了'); } }); $("ul").on("click", "li", function() { alert(11); }); // 1. 事件解绑 off // $("div").off(); // 这个是解除了div身上的所有事件 $("div").off("click"); // 这个是解除了div身上的点击事件 $("ul").off("click", "li"); // 2. one() 但是它只能触发事件一次 $("p").one("click", function() { alert(11); }) }) </script> </body> 1.2.4. 事件处理 trigger() 自动触发事件 有些时候,在某些特定的条件下,我们希望某些事件能够自动触发, 比如轮播图自动播放功能跟点击右侧按钮一致。可以利用定时器自动触发右侧按钮点击事件,不必鼠标点击触发。由此 jQuery 为我们提供了两个自动触发事件 trigger() 和 triggerHandler() ;语法演示代码<body> <div></div> <input type="text"> <script> $(function() { // 绑定事件 $("div").on("click", function() { alert(11); }); // 自动触发事件 // 1. 元素.事件() // $("div").click();会触发元素的默认行为 // 2. 元素.trigger("事件") // $("div").trigger("click");会触发元素的默认行为 $("input").trigger("focus"); // 3. 元素.triggerHandler("事件") 就是不会触发元素的默认行为 $("input").on("focus", function() { $(this).val("你好吗"); }); // 一个会获取焦点,一个不会 $("div").triggerHandler("click"); // $("input").triggerHandler("focus"); }); </script> </body> 1.3. jQuery 事件对象 jQuery 对DOM中的事件对象 event 进行了封装,兼容性更好,获取更方便,使用变化不大。事件被触发,就会有事件对象的产生。语法演示代码<body> <div></div> <script> $(function() { $(document).on("click", function() { console.log("点击了document"); }) $("div").on("click", function(event) { // console.log(event); console.log("点击了div"); event.stopPropagation(); }) }) </script> </body> 注意:jQuery中的 event 对象使用,可以借鉴 API 和 DOM 中的 event 。1.4. jQuery 拷贝对象 jQuery中分别为我们提供了两套快速获取和设置元素尺寸和位置的API,方便易用,内容如下。语法演示代码 <script> $(function() { // 1.合并数据 var targetObj = {}; var obj = { id: 1, name: "andy" }; // $.extend(target, obj); $.extend(targetObj, obj); console.log(targetObj); // 2. 会覆盖 targetObj 里面原来的数据 var targetObj = { id: 0 }; var obj = { id: 1, name: "andy" }; // $.extend(target, obj); $.extend(targetObj, obj); console.log(targetObj); }) </script> 1.5. jQuery 多库共存 实际开发中,很多项目连续开发十多年,jQuery版本不断更新,最初的 jQuery 版本无法满足需求,这时就需要保证在旧有版本正常运行的情况下,新的功能使用新的jQuery版本实现,这种情况被称为,jQuery 多库共存。语法演示代码<script> $(function() { // 让jquery 释放对$ 控制权 让用自己决定 var suibian = jQuery.noConflict(); console.log(suibian("span")); }) </script> 1.6. jQuery 插件 jQuery 功能比较有限,想要更复杂的特效效果,可以借助于 jQuery 插件完成。 这些插件也是依赖于jQuery来完成的,所以必须要先引入jQuery文件,因此也称为 jQuery 插件。 jQuery 插件常用的网站: jQuery 插件库 http://www.jq22.com/ jQuery 之家 http://www.htmleaf.com/ jQuery 插件使用步骤: 引入相关文件。(jQuery 文件 和 插件文件) 复制相关html、css、js (调用插件)。 1.4.1. 瀑布流插件(重点讲解) 我们学习的第一个插件是jQuery之家的开源插件,瀑布流。我们将重点详细讲解,从找到插件所在网页,然后点击下载代码,到插件的使用等,后面的插件使用可参考瀑布流插件的使用。下载位置代码演示 插件的使用三点: 1. 引入css. 2.引入JS 3.引入html。 (有的简单插件只需引入html和js,甚至有的只需引入js) 1.引入css. <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" type="text/css" href="css/default.css"> <!-- 下面的样式代码为页面布局,可以引入,也可以自己写,自己设计页面样式,一般为直接引入,方便 --> <style type="text/css"> #gallery-wrapper { position: relative; max-width: 75%; width: 75%; margin: 50px auto; } img.thumb { width: 100%; max-width: 100%; height: auto; } .white-panel { position: absolute; background: white; border-radius: 5px; box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); padding: 10px; } .white-panel h1 { font-size: 1em; } .white-panel h1 a { color: #A92733; } .white-panel:hover { box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5); margin-top: -5px; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; } </style> 2.引入js. <!-- 前两个必须引入 --> <script src="js/jquery-1.11.0.min.js"></script> <script src="js/pinterest_grid.js"></script> <!-- 下面的为启动瀑布流代码,参数可调节属性,具体功能可参考readme.html --> <script type="text/javascript"> $(function() { $("#gallery-wrapper").pinterest_grid({ no_columns: 5, padding_x: 15, padding_y: 10, margin_bottom: 50, single_column_breakpoint: 700 }); }); </script> 3.引入html. <!-- html结构一般为事先写好,很难修改结构,但可以修改内容及图片的多少(article标签) --> <section id="gallery-wrapper"> <article class="white-panel"> <img src="images/P_000.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_005.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_006.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_007.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> </section> 总结:jQuery插件就是引入别人写好的:html 、css、js (有时也可以只引入一部分,读懂后也可以修改部分内容)1.4.2. 图片懒加载插件 图片的懒加载就是:当页面滑动到有图片的位置,图片才进行加载,用以提升页面打开的速度及用户体验。(下载略)代码演示 懒加载只需引入html 和 js操作 即可,此插件不涉及css。 1.引入js <script src="js/EasyLazyload.min.js"></script> <script> lazyLoadInit({ showTime: 1100, onLoadBackEnd: function(i, e) { console.log("onLoadBackEnd:" + i); }, onLoadBackStart: function(i, e) { console.log("onLoadBackStart:" + i); } }); </script> 2.引入html <img data-lazy-src="upload/floor-1-3.png" alt=""> 1.4.3. 全屏滚动插件 全屏滚动插件比较大,所以,一般大型插件都会有帮助文档,或者网站。全屏滚动插件介绍比较详细的网站为:http://www.dowebok.com/demo/2014/77/代码演示 全屏滚动因为有多重形式,所以不一样的风格html和css也不一样,但是 js 变化不大。所以下面只演示js的引入,html和css引入根据自己实际项目需要使用哪种风格引入对应的HTML和CSS。<script src="js/jquery.min.js"></script> <script src="js/fullpage.min.js"></script> <script> $(function() { $('#dowebok').fullpage({ sectionsColor: ['pink', '#4BBFC3', '#7BAABE', '#f90'], navigation: true }); }); </script> 注意:实际开发,一般复制文件,然后在文件中进行修改和添加功能。1.4.4. bootstrap组件 Bootstrap是 Twitter 公司设计的基于HTML、CSS、JavaScript开发的简洁、直观、强悍的前端开发框架,他依靠jQuery实现,且支持响应式布局,使得 Web 开发更加方便快捷。 凡是在软件开发中用到了软件的复用,被复用的部分都可以称为组件,凡是在应用程序中已经预留接口的组件就是插件。Bootstrap组件使用非常方便: 1.引入bootstrap相关css和js 2.去官网复制html代码演示 引入bootstrap相关css和js <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <script src="bootstrap/js/jquery.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> 去官网复制html的功能模块 <div class="container"> <!-- Single button --> <div class="btn-group"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Action <span class="caret"></span> </button> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </div> </div> 1.4.5. bootstrap插件(JS) bootstrap中的js插件其实也是组件的一部分,只不过是需要js调用功能的组件,所以一般bootstrap的js插件一般会伴随着js代码(有的也可以省略js,用属性实现)。 步骤: 1.引入bootstrap相关css和js 2.去官网复制html 3.复制js代码,启动js插件。代码演示 引入bootstrap相关css和js <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <script src="bootstrap/js/jquery.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> 去官网复制html的功能模块 <!-- 模态框 --> <!-- Large modal --> <button type="button" class="btn btn-primary" data-toggle="modal" data-target=".bs-example-modal-lg">Large modal</button> <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> 里面就是模态框 </div> </div> </div> 复制js代码,启动js插件。 <script> // 当我们点击了自己定义的按钮,就弹出模态框 $(".myBtn").on("click", function() { // alert(11); $('#btn').modal() }) </script> 1.4.6. bootstrap案例-阿里百秀1.通过调用组件实现导航栏2.通过调用插件实现登录3.通过调用插件标签页实现 tab 栏 代码实现略。(详情参考源代码)1.7. 综合案例: toDoList案例分析(代码略)1.7.1 案例:案例介绍// 1. 文本框里面输入内容,按下回车,就可以生成待办事项。 // 2. 点击待办事项复选框,就可以把当前数据添加到已完成事项里面。 // 3. 点击已完成事项复选框,就可以把当前数据添加到待办事项里面。 // 4. 但是本页面内容刷新页面不会丢失。 1.7.2 案例:toDoList 分析// 1. 刷新页面不会丢失数据,因此需要用到本地存储 localStorage // 2. 核心思路: 不管按下回车,还是点击复选框,都是把本地存储的数据加载到页面中,这样保证刷新关闭页面不会丢失数据 // 3. 存储的数据格式:var todolist = [{ title : ‘xxx’, done: false}] // 4. 注意点1: 本地存储 localStorage 里面只能存储字符串格式 ,因此需要把对象转换为字符串 JSON.stringify(data)。 // 5. 注意点2: 获取本地存储数据,需要把里面的字符串转换为对象格式JSON.parse() 我们才能使用里面的数据。 1.7.3 案例:toDoList 按下回车把新数据添加到本地存储里面// 1.切记: 页面中的数据,都要从本地存储里面获取,这样刷新页面不会丢失数据,所以先要把数据保存到本地存储里面。 // 2.利用事件对象.keyCode判断用户按下回车键(13)。 // 3.声明一个数组,保存数据。 // 4.先要读取本地存储原来的数据(声明函数 getData()),放到这个数组里面。 // 5.之后把最新从表单获取过来的数据,追加到数组里面。 // 6.最后把数组存储给本地存储 (声明函数 savaDate()) 1.7.4 案例:toDoList 本地存储数据渲染加载到页面// 1.因为后面也会经常渲染加载操作,所以声明一个函数 load,方便后面调用 // 2.先要读取本地存储数据。(数据不要忘记转换为对象格式) // 3.之后遍历这个数据($.each()),有几条数据,就生成几个小li 添加到 ol 里面。 // 4.每次渲染之前,先把原先里面 ol 的内容清空,然后渲染加载最新的数据。 1.7.5 案例:toDoList 删除操作// 1.点击里面的a链接,不是删除的li,而是删除本地存储对应的数据。 // 2.核心原理:先获取本地存储数据,删除对应的数据,保存给本地存储,重新渲染列表li // 3.我们可以给链接自定义属性记录当前的索引号 // 4.根据这个索引号删除相关的数据----数组的splice(i, 1)方法 // 5.存储修改后的数据,然后存储给本地存储 // 6.重新渲染加载数据列表 // 7.因为a是动态创建的,我们使用on方法绑定事件 1.7.6 案例:toDoList 正在进行和已完成选项操作// 1.当我们点击了小的复选框,修改本地存储数据,再重新渲染数据列表。 // 2.点击之后,获取本地存储数据。 // 3.修改对应数据属性 done 为当前复选框的checked状态。 // 4.之后保存数据到本地存储 // 5.重新渲染加载数据列表 // 6.load 加载函数里面,新增一个条件,如果当前数据的done为true 就是已经完成的,就把列表渲染加载到 ul 里面 // 7.如果当前数据的done 为false, 则是待办事项,就把列表渲染加载到 ol 里面 1.7.7 案例:toDoList 统计正在进行个数和已经完成个数// 1.在我们load 函数里面操作 // 2.声明2个变量 :todoCount 待办个数 doneCount 已完成个数 // 3.当进行遍历本地存储数据的时候, 如果 数据done为 false, 则 todoCount++, 否则 doneCount++ // 4.最后修改相应的元素 text() 1.8. 今日总结
2022年06月02日
63 阅读
0 评论
0 点赞
2022-05-29
jQuery操作
1.1. jQuery 属性操作 jQuery 常用属性操作有三种:prop() / attr() / data() ;1.1.1 元素固有属性值 prop() 所谓元素固有属性就是元素本身自带的属性,比如 元素里面的 href ,比如 元素里面的 type。语法 注意:prop() 除了普通属性操作,更适合操作表单属性:disabled / checked / selected 等。1.1.2 元素自定义属性值 attr() 用户自己给元素添加的属性,我们称为自定义属性。 比如给 div 添加 index =“1”。语法 注意:attr() 除了普通属性操作,更适合操作自定义属性。(该方法也可以获取 H5 自定义属性)1.1.3 数据缓存 data() data() 方法可以在指定的元素上存取数据,并不会修改 DOM 元素结构。一旦页面刷新,之前存放的数据都将被移除。语法 注意:同时,还可以读取 HTML5 自定义属性 data-index ,得到的是数字型。演示代码<body> <a href="http://www.itcast.cn" title="都挺好">都挺好</a> <input type="checkbox" name="" id="" checked> <div index="1" data-index="2">我是div</div> <span>123</span> <script> $(function() { //1. element.prop("属性名") 获取元素固有的属性值 console.log($("a").prop("href")); $("a").prop("title", "我们都挺好"); $("input").change(function() { console.log($(this).prop("checked")); }); // console.log($("div").prop("index")); // 2. 元素的自定义属性 我们通过 attr() console.log($("div").attr("index")); $("div").attr("index", 4); console.log($("div").attr("data-index")); // 3. 数据缓存 data() 这个里面的数据是存放在元素的内存里面 $("span").data("uname", "andy"); console.log($("span").data("uname")); // 这个方法获取data-index h5自定义属性 第一个 不用写data- 而且返回的是数字型 console.log($("div").data("index")); }) </script> </body> 1.1.4 案例:购物车案例模块-全选1.全选思路:里面3个小的复选框按钮(j-checkbox)选中状态(checked)跟着全选按钮(checkall)走。2.因为checked 是复选框的固有属性,此时我们需要利用prop()方法获取和设置该属性。3.把全选按钮状态赋值给3小复选框就可以了。4.当我们每次点击小的复选框按钮,就来判断:5.如果小复选框被选中的个数等于3 就应该把全选按钮选上,否则全选按钮不选。6.:checked 选择器 :checked 查找被选中的表单元素。 代码实现略。(详情参考源代码)1.2. jQuery 文本属性值 jQuery的文本属性值常见操作有三种:html() / text() / val() ; 分别对应JS中的 innerHTML 、innerText 和 value 属性。1.2.1 jQuery内容文本值 常见操作有三种:html() / text() / val() ; 分别对应JS中的 innerHTML 、innerText 和 value 属性,主要针对元素的内容还有表单的值操作。语法 注意:html() 可识别标签,text() 不识别标签。演示代码<body> <div> <span>我是内容</span> </div> <input type="text" value="请输入内容"> <script> // 1. 获取设置元素内容 html() console.log($("div").html()); // $("div").html("123"); // 2. 获取设置元素文本内容 text() console.log($("div").text()); $("div").text("123"); // 3. 获取设置表单值 val() console.log($("input").val()); $("input").val("123"); </script> </body> 1.2.2. 案例:购物车案例模块-增减商品数量1.核心思路:首先声明一个变量,当我们点击+号(increment),就让这个值++,然后赋值给文本框。2.注意1: 只能增加本商品的数量, 就是当前+号的兄弟文本框(itxt)的值。3.修改表单的值是val() 方法4.注意2: 这个变量初始值应该是这个文本框的值,在这个值的基础上++。要获取表单的值5.减号(decrement)思路同理,但是如果文本框的值是1,就不能再减了。 代码实现略。(详情参考源代码)1.2.3. 案例:购物车案例模块-修改商品小计1.核心思路:每次点击+号或者-号,根据文本框的值 乘以 当前商品的价格 就是 商品的小计2.注意1: 只能增加本商品的小计, 就是当前商品的小计模块(p-sum)3.修改普通元素的内容是text() 方法4.注意2: 当前商品的价格,要把¥符号去掉再相乘 截取字符串 substr(1)5.parents(‘选择器’) 可以返回指定祖先元素6.最后计算的结果如果想要保留2位小数 通过 toFixed(2) 方法7.用户也可以直接修改表单里面的值,同样要计算小计。 用表单change事件8.用最新的表单内的值 乘以 单价即可 但是还是当前商品小计 代码实现略。(详情参考源代码)1.3. jQuery 元素操作 jQuery 元素操作主要讲的是用jQuery方法,操作标签的遍历、创建、添加、删除等操作。1.3.1. 遍历元素 jQuery 隐式迭代是对同一类元素做了同样的操作。 如果想要给同一类元素做不同操作,就需要用到遍历。语法1 注意:此方法用于遍历 jQuery 对象中的每一项,回调函数中元素为 DOM 对象,想要使用 jQuery 方法需要转换。语法2 注意:此方法用于遍历 jQuery 对象中的每一项,回调函数中元素为 DOM 对象,想要使用 jQuery 方法需要转换。演示代码<body> <div>1</div> <div>2</div> <div>3</div> <script> $(function() { // 如果针对于同一类元素做不同操作,需要用到遍历元素(类似for,但是比for强大) var sum = 0; var arr = ["red", "green", "blue"]; // 1. each() 方法遍历元素 $("div").each(function(i, domEle) { // 回调函数第一个参数一定是索引号 可以自己指定索引号号名称 // console.log(i); // 回调函数第二个参数一定是 dom 元素对象,也是自己命名 // console.log(domEle); // 使用jQuery方法需要转换 $(domEle) $(domEle).css("color", arr[i]); sum += parseInt($(domEle).text()); }) console.log(sum); // 2. $.each() 方法遍历元素 主要用于遍历数据,处理数据 // $.each($("div"), function(i, ele) { // console.log(i); // console.log(ele); // }); // $.each(arr, function(i, ele) { // console.log(i); // console.log(ele); // }) $.each({ name: "andy", age: 18 }, function(i, ele) { console.log(i); // 输出的是 name age 属性名 console.log(ele); // 输出的是 andy 18 属性值 }) }) </script> </body> 1.3.2. 案例:购物车案例模块-计算总计和总额1.把所有文本框中的值相加就是总额数量,总计同理。2.文本框里面的值不同,如果想要相加需要用 each() 遍历,声明一个变量做计数器,累加即可。 代码实现略。(详情参考源代码)1.3.3. 创建、添加、删除 jQuery方法操作元素的创建、添加、删除方法很多,则重点使用部分,如下:语法总和 注意:以上只是元素的创建、添加、删除方法的常用方法,其他方法请参详API。案例代码<body> <ul> <li>原先的li</li> </ul> <div class="test">我是原先的div</div> <script> $(function() { // 1. 创建元素 var li = $("<li>我是后来创建的li</li>"); // 2. 添加元素 // 2.1 内部添加 // $("ul").append(li); 内部添加并且放到内容的最后面 $("ul").prepend(li); // 内部添加并且放到内容的最前面 // 2.2 外部添加 var div = $("<div>我是后妈生的</div>"); // $(".test").after(div); $(".test").before(div); // 3. 删除元素 // $("ul").remove(); 可以删除匹配的元素 自杀 // $("ul").empty(); // 可以删除匹配的元素里面的子节点 孩子 $("ul").html(""); // 可以删除匹配的元素里面的子节点 孩子 }) </script> </body> 1.3.4 案例:购物车案例模块-删除商品模块1.核心思路:把商品remove() 删除元素即可2.有三个地方需要删除: 1. 商品后面的删除按钮 2. 删除选中的商品 3. 清理购物车3.商品后面的删除按钮: 一定是删除当前的商品,所以从 $(this) 出发4.删除选中的商品: 先判断小的复选框按钮是否选中状态,如果是选中,则删除对应的商品5.清理购物车: 则是把所有的商品全部删掉 代码实现略。(详情参考源代码)1.3.5 案例:购物车案例模块-选中商品添加背景1.核心思路:选中的商品添加背景,不选中移除背景即可2.全选按钮点击:如果全选是选中的,则所有的商品添加背景,否则移除背景3.小的复选框点击: 如果是选中状态,则当前商品添加背景,否则移除背景4.这个背景,可以通过类名修改,添加类和删除类 代码实现略。(详情参考源代码)1.4. jQuery 尺寸、位置操作 jQuery中分别为我们提供了两套快速获取和设置元素尺寸和位置的API,方便易用,内容如下。1.4.1. jQuery 尺寸操作 jQuery 尺寸操作包括元素宽高的获取和设置,且不一样的API对应不一样的盒子模型。语法代码演示<body> <div></div> <script> $(function() { // 1. width() / height() 获取设置元素 width和height大小 console.log($("div").width()); // $("div").width(300); // 2. innerWidth() / innerHeight() 获取设置元素 width和height + padding 大小 console.log($("div").innerWidth()); // 3. outerWidth() / outerHeight() 获取设置元素 width和height + padding + border 大小 console.log($("div").outerWidth()); // 4. outerWidth(true) / outerHeight(true) 获取设置 width和height + padding + border + margin console.log($("div").outerWidth(true)); }) </script> </body> 注意:有了这套 API 我们将可以快速获取和子的宽高,至于其他属性想要获取和设置,还要使用 css() 等方法配合。1.4.2. jQuery 位置操作 jQuery的位置操作主要有三个: offset()、position()、scrollTop()/scrollLeft() , 具体介绍如下:语法代码演示<body> <div class="father"> <div class="son"></div> </div> <div class="back">返回顶部</div> <div class="container"></div> <script> $(function() { // 1. 获取设置距离文档的位置(偏移) offset console.log($(".son").offset()); console.log($(".son").offset().top); // $(".son").offset({ // top: 200, // left: 200 // }); // 2. 获取距离带有定位父级位置(偏移) position 如果没有带有定位的父级,则以文档为准 // 这个方法只能获取不能设置偏移 console.log($(".son").position()); // $(".son").position({ // top: 200, // left: 200 // }); // 3. 被卷去的头部 $(document).scrollTop(100); // 被卷去的头部 scrollTop() / 被卷去的左侧 scrollLeft() // 页面滚动事件 var boxTop = $(".container").offset().top; $(window).scroll(function() { // console.log(11); console.log($(document).scrollTop()); if ($(document).scrollTop() >= boxTop) { $(".back").fadeIn(); } else { $(".back").fadeOut(); } }); // 返回顶部 $(".back").click(function() { // $(document).scrollTop(0); $("body, html").stop().animate({ scrollTop: 0 }); // $(document).stop().animate({ // scrollTop: 0 // }); 不能是文档而是 html和body元素做动画 }) }) </script> </body> 1.4.3. 案例:带有动画的返回顶部1.核心原理: 使用animate动画返回顶部。2.animate动画函数里面有个scrollTop 属性,可以设置位置3.但是是元素做动画,因此 $(“body,html”).animate({scrollTop: 0}) 代码实现略。(详情参考源代码)1.4.4. 案例: 品优购电梯导航(上)1.当我们滚动到 今日推荐 模块,就让电梯导航显示出来2.点击电梯导航页面可以滚动到相应内容区域3.核心算法:因为电梯导航模块和内容区模块一一对应的4.当我们点击电梯导航某个小模块,就可以拿到当前小模块的索引号5.就可以把animate要移动的距离求出来:当前索引号内容区模块它的offset().top6.然后执行动画即可 代码实现略。(详情参考源代码)1.4.5. 案例:品优购电梯导航(下)1.当我们点击电梯导航某个小li, 当前小li 添加current类,兄弟移除类名2.当我们页面滚动到内容区域某个模块, 左侧电梯导航,相对应的小li模块,也会添加current类, 兄弟移除current类。3.触发的事件是页面滚动,因此这个功能要写到页面滚动事件里面。4.需要用到each,遍历内容区域大模块。 each里面能拿到内容区域每一个模块元素和索引号5.判断的条件: 被卷去的头部 大于等于 内容区域里面每个模块的offset().top6.就利用这个索引号找到相应的电梯导航小li添加类。 代码实现略。(详情参考源代码)1.5. 今日总结
2022年05月29日
121 阅读
0 评论
0 点赞
2022-05-27
jQuery简介
1.1. jQuery 介绍1.1.1 JavaScript 库 JavaScript库:即 library,是一个封装好的特定的集合(方法和函数)。从封装一大堆函数的角度理解库,就是在这个库中,封装了很多预先定义好的函数在里面,比如动画animate、hide、show,比如获取元素等。简单理解: 就是一个JS 文件,里面对我们原生js代码进行了封装,存放到里面。这样我们可以快速高效的使用这些封装好的功能了。比如 jQuery,就是为了快速方便的操作DOM,里面基本都是函数(方法)。 常见的JavaScript 库:jQuery、Prototype、YUI、Dojo、Ext JS、移动端的zepto等,这些库都是对原生 JavaScript 的封装,内部都是用 JavaScript 实现的,我们主要学习的是 jQuery。1.1.2 jQuery的概念 jQuery总体概况如下 : jQuery 是一个快速、简洁的 JavaScript 库,其设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。 j 就是 JavaScript; Query 查询; 意思就是查询js,把js中的DOM操作做了封装,我们可以快速的查询使用里面的功能。 jQuery 封装了 JavaScript 常用的功能代码,优化了 DOM 操作、事件处理、动画设计和 Ajax 交互。 学习jQuery本质: 就是学习调用这些函数(方法)。 jQuery 出现的目的是加快前端人员的开发速度,我们可以非常方便的调用和使用它,从而提高开发效率。 1.1.3 jQuery的优点 轻量级。核心文件才几十kb,不会影响页面加载速度。 跨浏览器兼容,基本兼容了现在主流的浏览器。 链式编程、隐式迭代。 对事件、样式、动画支持,大大简化了DOM操作。 支持插件扩展开发。有着丰富的第三方的插件,例如:树形菜单、日期控件、轮播图等。 免费、开源。 1.2. jQuery 的基本使用1.2.1 jQuery 的下载 jQuery的官网地址: https://jquery.com/,官网即可下载最新版本。各个版本的下载:https://code.jquery.com/ 版本介绍:1x :兼容 IE 678 等低版本浏览器, 官网不再更新2x :不兼容 IE 678 等低版本浏览器, 官网不再更新3x :不兼容 IE 678 等低版本浏览器, 是官方主要更新维护的版本1.2.2. 体验jQuery 步骤: 引入jQuery文件。 在文档最末尾插入 script 标签,书写体验代码。 $('div').hide() 可以隐藏盒子。 1.2.3. jQuery的入口函数 jQuery中常见的两种入口函数:// 第一种: 简单易用。 $(function () { ... // 此处是页面 DOM 加载完成的入口 }) ; // 第二种: 繁琐,但是也可以实现 $(document).ready(function(){ ... // 此处是页面DOM加载完成的入口 }); 总结: 等着 DOM 结构渲染完毕即可执行内部代码,不必等到所有外部资源加载完成,jQuery 帮我们完成了封装。 相当于原生 js 中的 DOMContentLoaded。 不同于原生 js 中的 load 事件是等页面文档、外部的 js 文件、css文件、图片加载完毕才执行内部代码。 更推荐使用第一种方式。 1.2.4. jQuery中的顶级对象$ $是 jQuery 的别称,在代码中可以使用 jQuery 代替,但一般为了方便,通常都直接使用 $ 。 $是jQuery的顶级对象,相当于原生JavaScript中的 window。把元素利用$包装成jQuery对象,就可以调用jQuery 的方法。 1.2.5. jQuery 对象和 DOM 对象 使用 jQuery 方法和原生JS获取的元素是不一样的,总结如下 : 用原生 JS 获取来的对象就是 DOM 对象 jQuery 方法获取的元素就是 jQuery 对象。 jQuery 对象本质是: 利用$对DOM 对象包装后产生的对象(伪数组形式存储)。 注意:只有 jQuery 对象才能使用 jQuery 方法,DOM 对象则使用原生的 JavaScirpt 方法。1.2.6. jQuery 对象和 DOM 对象转换 DOM 对象与 jQuery 对象之间是可以相互转换的。因为原生js 比 jQuery 更大,原生的一些属性和方法 jQuery没有给我们封装. 要想使用这些属性和方法需要把jQuery对象转换为DOM对象才能使用。// 1.DOM对象转换成jQuery对象,方法只有一种 var box = document.getElementById('box'); // 获取DOM对象 var jQueryObject = $(box); // 把DOM对象转换为 jQuery 对象 // 2.jQuery 对象转换为 DOM 对象有两种方法: // 2.1 jQuery对象[索引值] var domObject1 = $('div')[0] // 2.2 jQuery对象.get(索引值) var domObject2 = $('div').get(0) 总结:实际开发比较常用的是把DOM对象转换为jQuery对象,这样能够调用功能更加强大的jQuery中的方法。1.3. jQuery 选择器 原生 JS 获取元素方式很多,很杂,而且兼容性情况不一致,因此 jQuery 给我们做了封装,使获取元素统一标准。1.3.1. 基础选择器$("选择器") // 里面选择器直接写 CSS 选择器即可,但是要加引号 1.3.2. 层级选择器 层级选择器最常用的两个分别为:后代选择器和子代选择器。 基础选择器和层级选择器案例代码<body> <div>我是div</div> <div class="nav">我是nav div</div> <p>我是p</p> <ul> <li>我是ul 的</li> <li>我是ul 的</li> <li>我是ul 的</li> </ul> <script> $(function() { console.log($(".nav")); console.log($("ul li")); }) </script> </body> 1.3.3. 筛选选择器 筛选选择器,顾名思义就是在所有的选项中选择满足条件的进行筛选选择。常见如下 :案例代码<body> <ul> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> </ul> <ol> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> </ol> <script> $(function() { $("ul li:first").css("color", "red"); $("ul li:eq(2)").css("color", "blue"); $("ol li:odd").css("color", "skyblue"); $("ol li:even").css("color", "pink"); }) </script> </body> 另: jQuery中还有一些筛选方法,类似DOM中的通过一个节点找另外一个节点,父、子、兄以外有所加强。偏重于记忆,演示代码略。1.3.4 知识铺垫 jQuery 设置样式 $('div').css('属性', '值') jQuery 里面的排他思想 // 想要多选一的效果,排他思想:当前元素设置样式,其余的兄弟元素清除样式。 $(this).css(“color”,”red”); $(this).siblings(). css(“color”,””); 隐式迭代 // 遍历内部 DOM 元素(伪数组形式存储)的过程就叫做隐式迭代。 // 简单理解:给匹配到的所有元素进行循环遍历,执行相应的方法,而不用我们再进行循环,简化我们的操作,方便我们调用。 $('div').hide(); // 页面中所有的div全部隐藏,不用循环操作 链式编程 // 链式编程是为了节省代码量,看起来更优雅。 $(this).css('color', 'red').sibling().css('color', ''); 1.3.5 案例:淘宝服饰精品案例思路分析:1.核心原理:鼠标经过左侧盒子某个小li,就让内容区盒子相对应图片显示,其余的图片隐藏。2.需要得到当前小li 的索引号,就可以显示对应索引号的图片3.jQuery 得到当前元素索引号 $(this).index()4.中间对应的图片,可以通过 eq(index) 方法去选择5.显示元素 show() 隐藏元素 hide() 代码实现略。(详情参考源代码)1.4. jQuery 样式操作 jQuery中常用的样式操作有两种:css() 和 设置类样式方法1.4.1. 方法1: 操作 css 方法 jQuery 可以使用 css 方法来修改简单元素样式; 也可以操作类,修改多个样式。 常用以下三种形式 :// 1.参数只写属性名,则是返回属性值 var strColor = $(this).css('color'); // 2. 参数是属性名,属性值,逗号分隔,是设置一组样式,属性必须加引号,值如果是数字可以不用跟单位和引号 $(this).css(''color'', ''red''); // 3. 参数可以是对象形式,方便设置多组样式。属性名和属性值用冒号隔开, 属性可以不用加引号 $(this).css({ "color":"white","font-size":"20px"}); 注意:css() 多用于样式少时操作,多了则不太方便。1.4.2. 方法2: 设置类样式方法 作用等同于以前的 classList,可以操作类样式, 注意操作类里面的参数不要加点。 常用的三种设置类样式方法:// 1.添加类 $("div").addClass("current"); // 2.删除类 $("div").removeClass("current"); // 3.切换类 $("div").toggleClass("current"); 注意: 设置类样式方法比较适合样式多时操作,可以弥补css()的不足。 原生 JS 中 className 会覆盖元素原先里面的类名,jQuery 里面类操作只是对指定类进行操作,不影响原先的类名。 1.4.3. 案例:tab 栏切换思路分析:1.点击上部的li,当前li 添加current类,其余兄弟移除类。2.点击的同时,得到当前li 的索引号3.让下部里面相应索引号的item显示,其余的item隐藏 代码实现略。(详情参考源代码)1.5. jQuery 效果 jQuery 给我们封装了很多动画效果,最为常见的如下: 显示隐藏:show() / hide() / toggle() ; 划入画出:slideDown() / slideUp() / slideToggle() ; 淡入淡出:fadeIn() / fadeOut() / fadeToggle() / fadeTo() ; 自定义动画:animate() ; 注意:动画或者效果一旦触发就会执行,如果多次触发,就造成多个动画或者效果排队执行。jQuery为我们提供另一个方法,可以停止动画排队:stop() ;1.5.1. 显示隐藏 显示隐藏动画,常见有三个方法:show() / hide() / toggle() ; 语法规范如下:代码演示<body> <button>显示</button> <button>隐藏</button> <button>切换</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { $("div").show(1000, function() { alert(1); }); }) $("button").eq(1).click(function() { $("div").hide(1000, function() { alert(1); }); }) $("button").eq(2).click(function() { $("div").toggle(1000); }) // 一般情况下,我们都不加参数直接显示隐藏就可以了 }); </script> </body> 1.5.2. 滑入滑出 滑入滑出动画,常见有三个方法:slideDown() / slideUp() / slideToggle() ; 语法规范如下:代码演示<body> <button>下拉滑动</button> <button>上拉滑动</button> <button>切换滑动</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { // 下滑动 slideDown() $("div").slideDown(); }) $("button").eq(1).click(function() { // 上滑动 slideUp() $("div").slideUp(500); }) $("button").eq(2).click(function() { // 滑动切换 slideToggle() $("div").slideToggle(500); }); }); </script> </body> 小案例:下拉菜单略(详情参看源码)。1.5.3 淡入淡出 淡入淡出动画,常见有四个方法:fadeIn() / fadeOut() / fadeToggle() / fadeTo() ; 语法规范如下:代码演示<body> <button>淡入效果</button> <button>淡出效果</button> <button>淡入淡出切换</button> <button>修改透明度</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { // 淡入 fadeIn() $("div").fadeIn(1000); }) $("button").eq(1).click(function() { // 淡出 fadeOut() $("div").fadeOut(1000); }) $("button").eq(2).click(function() { // 淡入淡出切换 fadeToggle() $("div").fadeToggle(1000); }); $("button").eq(3).click(function() { // 修改透明度 fadeTo() 这个速度和透明度要必须写 $("div").fadeTo(1000, 0.5); }); }); </script> </body> 1.5.4 自定义动画 自定义动画非常强大,通过参数的传递可以模拟以上所有动画,方法为:animate() ; 语法规范如下:代码演示<body> <button>动起来</button> <div></div> <script> $(function() { $("button").click(function() { $("div").animate({ left: 500, top: 300, opacity: .4, width: 500 }, 500); }) }) </script> </body> 1.5.5 停止动画排队 动画或者效果一旦触发就会执行,如果多次触发,就造成多个动画或者效果排队执行。 停止动画排队的方法为:stop() ; stop() 方法用于停止动画或效果。 stop() 写到动画或者效果的前面, 相当于停止结束上一次的动画。 总结: 每次使用动画之前,先调用 stop() ,在调用动画。1.5.6. 事件切换 jQuery中为我们添加了一个新事件 hover() ; 功能类似 css 中的伪类 :hover 。介绍如下语法hover([over,]out) // 其中over和out为两个函数 over:鼠标移到元素上要触发的函数(相当于mouseenter) out:鼠标移出元素要触发的函数(相当于mouseleave) 如果只写一个函数,则鼠标经过和离开都会触发它 hover事件和停止动画排列案例<body> <ul class="nav"> <li> <a href="#">微博</a> <ul><li><a href="">私信</a></li><li><a href="">评论</a></li><li><a href="">@我</a></li></ul> </li> <li> <a href="#">微博</a> <ul><li><a href="">私信</a></li><li><a href="">评论</a></li><li><a href="">@我</a></li></ul> </li> </ul> <script> $(function() { // 鼠标经过 // $(".nav>li").mouseover(function() { // // $(this) jQuery 当前元素 this不要加引号 // // show() 显示元素 hide() 隐藏元素 // $(this).children("ul").slideDown(200); // }); // // 鼠标离开 // $(".nav>li").mouseout(function() { // $(this).children("ul").slideUp(200); // }); // 1. 事件切换 hover 就是鼠标经过和离开的复合写法 // $(".nav>li").hover(function() { // $(this).children("ul").slideDown(200); // }, function() { // $(this).children("ul").slideUp(200); // }); // 2. 事件切换 hover 如果只写一个函数,那么鼠标经过和鼠标离开都会触发这个函数 $(".nav>li").hover(function() { // stop 方法必须写到动画的前面 $(this).children("ul").stop().slideToggle(); }); }) </script> </body> 1.5.7. 案例:王者荣耀手风琴效果思路分析:1.鼠标经过某个小li 有两步操作:2.当前小li 宽度变为 224px, 同时里面的小图片淡出,大图片淡入3.其余兄弟小li宽度变为69px, 小图片淡入, 大图片淡出 代码实现略。(详情参考源代码)1.6. 今日总结
2022年05月27日
89 阅读
0 评论
0 点赞
1
2
3
...
5