如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
更新阶段分为两部分 父组件执行 render 或者调用 this.setState。
componentWillReceiveProps
大部分网上教程为外部 props 发生改变才触发 componentWillReceiveProps,其实不是,当父组件进入 render 时,无论子组件的 props 发没发生改变,都会执行该生命周期函数。
函数参数有一个,为 nextProps,为将要新的 props。
值得注意的是,在整个更新阶段的生命周期函数,只有在此函数内可以调用 this.setState 方法,当然其他也可以调用,但是会造成死循环 。
shouldComponentUpdate
该函数需要返回值,如没定义则默认返回 true。当返回值为 true 时,进入 componentWillIpdate ,如为 false ,则什么都不发生。所以说这是一个可以进行 React 性能优化的地方。函数参数有两个 nextProps 和 nextState。我们需用做的就是在 this.props、this.state、nextState、nextProps之间进行对比,来解决重复渲染的目的。
componentWillUpdate
如果 shouldComponentUpdate 返回值为 true 的话,生命周期会进入该函数中。在这个函数中我们可以根据实际情况做一些事情,但是不能调用 this.setState。
render
在更新阶段的 render 来讲一讲 调和 过程。 render 返回的 JSX 标签会保存在内存中,react 会通过 diff 算法来计算出最小化改动完成差异的更新。diff 是逐层递归比较,首先比较类型是否一样。如果发现 <div>和 <span> 的差别的话,react 会选择直接放弃之前的 dom 元素, 重新渲染。所以说即使是更新阶段的调和过程,也会触发组件的挂载、卸载阶段。
componentDidUpdate
在这个时候已经更新完 dom 结构,可以重新使用 dom 操作。
总结
总体来说更新的生命周期要做的最重要的事情就是性能优化,减少重复渲染次数。
在这个方面已经有很多成熟的解决方法了,在我的博客中也会介绍如何定制更新阶段的生命周期函数。
在使用上,最最重要的一点就是不要在除了 componentWillReceiveProps 之外的其他更新阶段生命周期函数内调用 this.setState。
相关链接:
浅入 React 生命周期相关(一)挂载生命周期
---------------------
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
前几天面试问道 react 的相关知识,对我打击比较大,感觉对 react 认识非常肤浅,所以在这里重新梳理一下,想想之前没有仔细思考过的东西。
另外有说的不对的地方还请帮我指正一下,先谢谢各位啦。
目录索引:
React 有一套合理的运行机制去控制程序在指定的时刻该做什么事,当一个生命周期钩子被触发后,紧接着会有下一个钩子,直到整个生命周期结束。
生命周期代表着每个执行阶段,比如组件初始化,更新完成,马上要卸载等等,React 会在指定的时机执行相关的生命周期钩子,使我们可以有机在程序运行中会插入自己的逻辑。
我们写代码的时候往往会有很多组件以及他们的子组件,各自调用不同的生命周期,这时就要解决谁先谁后的问题,在 react v16 之前是采用了递归调用的方式一个一个执行,而在现在 v16 的版本中则采用了与之完全不同的处理(调度)方式,名叫 Fiber,这个东西 facebook 做了有两年时间,实现非常复杂。
具体 Fiber 它是一个什么东西呢?不要着急,我们先从最基本的生命周期钩子看起。
首先看一下 React V16.4 后的生命周期概况(图片来源)
constructor()
- 类构造器初始化
static getDerivedStateFromProps()
- 组件初始化时主动触发
render()
- 递归生成虚拟 DOM
componentDidMount()
- 完成首次 DOM 渲染
static getDerivedStateFromProps()
- 每次 render() 之前执行
shouldComponentUpdate()
- 校验是否需要执行更新操作
render()
- 递归生成虚拟 DOM
getSnapshotBeforeUpdate()
- 在渲染真实 DOM 之前
componentDidUpdate()
- 完成 DOM 渲染
componentWillUnmount()
- 组件销毁之前被直接调用
static getDerivedStateFromProps()
这个钩子会在每个更新操作之前(即使props没有改变)执行一次,使用时应该保持谨慎。
componentDidMount()
和 componentDidUpdate()
执行的时机是差不多的,都在 render
之后,只不过前者只在首次渲染后执行,后者首次渲染不会执行
getSnapshotBeforeUpdate()
执行时可以获得只读的新 DOM 树,此函数的返回值为 componentDidUpdate(prevProps, prevState, snapshot)
的第三个参数
关于 Fiber,强烈建议听一下知乎上程墨Morgan的 live 《深入理解React v16 新功能》,这里潜水员的例子和图片也是引用于此 live。
我们知道 React 是通过递归的方式来渲染组件的,在 V16 版本之前的版本里,当一个状态发生变更时,react 会从当前组件开始,依次递归调用所有的子组件生命周期钩子,而且这个过程是同步执行的且无法中断的,一旦有很深很深的组件嵌套,就会造成严重的页面卡顿,影响用户体验。
React 在V16版本之前的版本里引入了 Fiber 这样一个东西,它的英文涵义为纤维,在计算机领域它排在在进程和线程的后面,虽然 React 的 Fiber 和计算机调度里的概念不一样,但是可以方便对比理解,我们大概可以想象到 Fiber 可能是一个比线程还短的时间片段。
Fiber 把当前需要执行的任务分成一个个微任务,安排优先级,然后依次处理,每过一段时间(非常短,毫秒级)就会暂停当前的任务,查看有没有优先级较高的任务,然后暂停(也可能会完全放弃)掉之前的执行结果,跳出到下一个微任务。同时 Fiber 还做了一些优化,可以保持住之前运行的结果以到达复用目的。
我们可以把调度当成一个潜水员在海底寻宝,v16 之前是通过组件递归的方式进行寻宝,从父组件开始一层一层深入到最里面的子组件,也就是如下图所示。
而替换成了 Fiber 后,海底变成的狭缝(简单理解为递归变成了遍历),潜水员会每隔一小段时间浮出水面,看看有没有其他寻宝任务。注意此时没有寻到宝藏的话,那么之前潜水的时间就浪费了。就这样潜水员会一直下潜和冒泡,具体如下图所示。
从生命周期那张图片纵向来看,Fiber 将整个生命周期分成了三个阶段:
componentWillMount()
,componentWillUpdate()
,componentWillReceiveProps()
的三个生命周期钩子被加上了 UNSAFE
标记
简而言之:以 render() 为界,之前执行的生命周期都有可能会打断并多次调用,之后的生命周期是不可被打断的且只会调用一次。所以尽量把副作用的代码放在只会执行一次的 commit 阶段。
除了上面常用的钩子,React 还提供了如下钩子:
static getDerivedStateFromError()
在 render 阶段执行,通过返回 state 更新组件状态
componentDidCatch()
在 commit 阶段执行,可以放一些有副作用的代码
理解了生命周期和三个执行阶段,就可以比较容易理解组件状态的更新机制了。
这个方法可以让我们更新组件的 state 状态。第一个参数可以是对象,也可以是 updater 函数,如果是函数,则会接受当前的 state 和 props 作为参数。第二个参数为函数,是在 commit 阶段后执行,准确的说是在 componentDidUpdate()
后执行。
setState() 的更新过程是异步的(除非绑定在 DOM 事件中或写在 setTimeout 里),而且会在最后合并所有的更新,如下:
Object.assign( previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
复制代码
之所以设计成这样,是为了避免在一次生命周期中出现多次的重渲染,影响页面性能。
如果我们想强制刷新一个组件,可以直接调用该方法,调用时会直接执行 render()
这个函数而跳过 shouldComponentUpdate()
。
function wait() { return new Promise(resolve => {
setTimeout(() => {
resolve(); console.log("wait");
}, 0);
});
} //......省略组件创建 async componentDidMount() { await wait(); this.setState({ name: "new name" }); console.log("componentDidMount");
}
componentDidUpdate() { console.log("componentDidUpdate");
}
render() { console.log(this.state); return null } //......省略组件创建 // 输出结果如下 // wait // {name: "new name"} // componentDidUpdate // componentDidMount // 注意 componentDidUpdate 的输出位置,一般情况下 // componentDidUpdate 都是在componentDidMount 后面 // 执行的,但是这里因为setState 写在了 await 后面 // 所以情况相反。 复制代码
了解 react 生命周期和更新机制确实有利于编写代码,特别是当代码量越来越大时,错用的 setState 或生命周期钩子都可能埋下越来越多的雷,直到有一天无法维护。。。
我的个人建议如下:
getDerivedStateFromProps()
当成是 UNSAFE_componentWillReceiveProps()
的替代品,因为 getDerivedStateFromProps()
会在每次 render() 之前执行,即使 props 没有改变
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读书籍书单大全:
书单导航页(点击右侧 极客侠栈 即可打开个人博客):极客侠栈
①【Java】学习之路吐血整理技术书从入门到进阶最全50+本(珍藏版)
②【算法数据结构+acm】从入门到进阶吐血整理书单50+本(珍藏版)
③【数据库】从入门到进阶必读18本技术书籍网盘吐血整理网盘(珍藏版)
④【Web前端】从HTML到JS到AJAX到HTTP从框架到全栈帮你走更少弯路(珍藏版)
⑤【python】书最全已整理好(从入门到进阶)(珍藏版)
⑥【机器学习】+python整理技术书(从入门到进阶已经整理好)(珍藏版)
⑦【C语言】推荐书籍从入门到进阶带你走上大牛之路(珍藏版)
⑧【安卓】入门到进阶推荐书籍整理pdf书单整理(珍藏版)
⑨【架构师】之路史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)
⑩【C++】吐血整理推荐书单从入门到进阶成神之路100+本(珍藏)
⑪【ios】IOS书单从入门到进阶吐血整理(珍藏版)
--------------------------------------------------------------------------------------------------------------------
如果您已经入门reactjs,请绕道~ 这篇博客只适合初学者,初学reactjs的时候,如果你不会webpack,相信很多人都会被官方的例子绕的晕头转向。 ES6的例子也会搞死一批入门者。之前一直用的gulp,突然换了webpack,我也非常不习惯。在这块也卡住了,对于想学reactjs的朋友,我的学习建议是这样的:
nodejs => webpack => ES6 => reactjs
官方的很多例子都是ES6语法+webpack打包的,所以在学习reactjs之前,最好是会ES6和webpack,这样能事半功倍!
1、首先来说说nodejs
先安装版本的nodejs,npm一般都是自带的。安装成全局的比较方便构建项目。
npm install -g grunt-cli # 全局安装
npm可安装的插件可以在这里去找找 www.npmjs.com/ 如果不能安装,可以使用淘宝的镜象资源
2、webpack
webpack是一款打包工具,可以做一些js压缩,sass,less转换,图片处理,js代码合成,ES6转ES5语法等很多功能,如果用过grunt,或者gulp的朋友,webpack也就不陌生了。都是需要写配置文件。
3、ES6
github上很多案例都是用到了ES6的语法,所以,这里我们可以通过webpack的工具 babel ,把ES6的语法转化为ES5的语法,这样我们就可以使用github上面的demo了。
比如:
import '../css/common.scss';
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Link, IndexRoute } from 'react-router'
import { browserHistory } from 'react-router'
这里的import 就是ES6的语法,在webpack里面使用babel工具将其转化为 ES5的语法。我这里用了JSX(reactjs 提供的一种简洁的语法)如果对JSX不了解的,可以去百度下。
4、快速开发
每次我们在修改JSX文件,或者SASS文件后,都要执行webpack命令进行打包,这样的开发效率很慢,官方提供了一个很牛X的工具,react-hot-loader + webpack-dev-server 可以帮助你快速开发,自动刷新页面。
5、DEMO小试牛刀
这里我把自己做的一个DEMO分享给大家,如果你已经安装了nodejs,并且npm也是全局的。下载后解压,打开 start.bat,输入 npm install 安装所需的插件,安装成功后执行 npm start ,等项目跑起来后,在浏览器输入 http://127.0.0.1:3000 就可以访问项目了。
这里是一个 react-router 的一个例子。
github 地址:https://github.com/mtsee/react-router-demo
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
按照这个来配置就好了,这里相当于是帮你把webpack需要的繁琐的配置给你都弄好了,然后你直接从那里全部下下来就可以
记得先下好nodejs,安装的代码记得全都c v,不要自己敲
安装完nodejs之后再安装淘宝npm镜像,安装完了之后后面的安装速度会快一点
npm install -g cnpm --registry=https://registry.npm.taobao.org
1
然后按照下面的五步流程一个一个来
1、全局安装vuecli
npm install -g vue-cli
1
2、进入目录–初始化项目
vue init webpack my-project //这个要进入项目目录再用,会创建一个my-project的文件夹
1
3、进入项目
cd my-project
1
4、安装依赖
npm install
1
5、启动项目
npm run dev
1
以上执行完后会出现一个 my-project 项目文件夹,用vscode打开后会看到以下目录
下面解释一下这些文件及目录分别是干什么的
目录结构的分析
1、bind
├── build // 项目构建(webpack)相关代码 记忆:(够贱) 9个
│ ├── build.js // 生产环境构建代码
│ ├── checkversions.js // 检查node&npm等版本
│ ├── devclient.js // 热加载相关
│ ├── devserver.js // 构建本地服务器
│ ├── utils.js // 构建配置公用工具
│ ├── vueloader.conf.js // vue加载器
│ ├── webpack.base.conf.js // webpack基础环境配置
│ ├── webpack.dev.conf.js // webpack开发环境配置
│ └── webpack.prod.conf.js // webpack生产环境配置
2、config
├── config// 项目开发环境配置相关代码 记忆: (环配) 3个
│ ├── dev.env.js // 开发环境变量(看词明意)
│ ├── index.js //项目一些配置变量
│ └── prod.env.js // 生产环境变量
3、node_modules
├──node_modules// 项目依赖的模块 记忆: (依赖) *个
4、src
├── src// 源码目录 5
1
│ ├── assets// 资源目录
│ │ └── logo.png
2
│ ├── components// vue公共组件
│ │ └── Hello.vue
3
│ ├──router// 前端路由
│ │ └── index.js// 路由配置文件
4
│ ├── App.vue// 页面入口文件(根组件)
5
│ └── main.js// 程序入口文件(入口js文件)
5、static
└── static// 静态文件,比如一些图片,json数据等
│ ├── .gitkeep
6、剩余的文件
├── .babelrc// ES6语法编译配置
├── .editorconfig// 定义代码格式
├── .gitignore// git上传需要忽略的文件格式
├── index.html// 入口页面
├── package.json// 项目基本信息
├── README.md// 项目说明
---------------------
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
加粗样式
***@TOC
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
全新的界面设计 ,将会带来全新的写作体验;
在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
全新的 KaTeX数学公式 语法;
增加了支持甘特图的mermaid语法1 功能;
增加了 多屏幕编辑 Markdown文章功能;
增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block
var foo = 'bar';
1
2
生成一个适合你的列表
项目
项目
项目
项目1
项目2
项目3
计划任务
完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 Value
电脑 $1600
手机 $12
导管 $1
设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右
第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash
创建一个自定义列表
Markdown
Text-to-HTML conversion tool
Authors
John
Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ(n)=(n−1)!∀n∈N \Gamma(n) = (n-1)!\quad\foralln\in\mathbb NΓ(n)=(n−1)!∀n∈N 是通过欧拉积分
Unexpected text node: ' 'Unexpected text node: ' '
Γ(z)=∫
0
∞
t
z−1
e
−t
dt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
Mon 06
Mon 13
Mon 20
已完成
进行中
计划一
计划二
现有任务
Adding GANTT diagram functionality to mermaid
关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
张三
李四
王五
你好!李四, 最近怎么样?
你最近怎么样,王五?
我很好,谢谢!
我很好,谢谢!
李四想了很长时间,文字太长了不适合放在一行.
打量着王五...
很好... 王五, 你怎么样?
张三
李四
王五
这将产生一个流程图。:
链接
长方形
圆
圆角长方形
菱形
关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
开始
我的操作
确认?
结束
yes
no
关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
mermaid语法说明 ↩︎
注脚的解释 ↩︎
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
1. 如何实现一个响应式对象
最近在看 Vue 的源码,其中最核心基础的一块就是 Observer/Watcher/Dep, 简而言之就是,Vue 是如何拦截数据的读写, 如果实现对应的监听,并且特定的监听执行特定的回调或者渲染逻辑的。总的可以拆成三大块来说。这一块,主要说的是 Vue 是如何将一个 plain object 给处理成 reactive object 的,也就是,Vue 是如何拦截拦截对象的 get/set 的
我们知道,用 Object.defineProperty 拦截数据的 get/set 是 vue 的核心逻辑之一。这里我们先考虑一个最简单的情况 一个 plain obj 的数据,经过你的程序之后,使得这个 obj 变成 Reactive Obj (不考虑数组等因素,只考虑最简单的基础数据类型,和对象):
如果这个 obj 的某个 key 被 get, 则打印出 get ${key} - ${val} 的信息
如果这个 obj 的某个 key 被 set, 如果监测到这个 key 对应的 value 发生了变化,则打印出 set ${key} - ${val} - ${newVal} 的信息。
对应的简要代码如下:
Observer.js
export class Observer {
constructor(obj) {
this.obj = obj;
this.transform(obj);
}
// 将 obj 里的所有层级的 key 都用 defineProperty 重新定义一遍, 使之 reactive
transform(obj) {
const _this = this;
for (let key in obj) {
const value = obj[key];
makeItReactive(obj, key, value);
}
}
}
function makeItReactive(obj, key, val) {
// 如果某个 key 对应的 val 是 object, 则重新迭代该 val, 使之 reactive
if (isObject(val)) {
const childObj = val;
new Observer(childObj);
}
// 如果某个 key 对应的 val 不是 Object, 而是基础类型,我们则对这个 key 进行 defineProperty 定义
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.info(`get ${key}-${val}`)
return val;
},
set: (newVal) => {
// 如果 newVal 和 val 相等,则不做任何操作(不执行渲染逻辑)
if (newVal === val) {
return;
}
// 如果 newVal 和 val 不相等,且因为 newVal 为 Object, 所以先用 Observer迭代 newVal, 使之 reactive, 再用 newVal 替换掉 val, 再执行对应操作(渲染逻辑)
else if (isObject(newVal)) {
console.info(`set ${key} - ${val} - ${newVal} - newVal is Object`);
new Observer(newVal);
val = newVal;
}
// 如果 newVal 和 val 不相等,且因为 newVal 为基础类型, 所以用 newVal 替换掉 val, 再执行对应操作(渲染逻辑)
else if (!isObject(newVal)) {
console.info(`set ${key} - ${val} - ${newVal} - newVal is Basic Value`);
val = newVal;
}
}
})
}
function isObject(data) {
if (typeof data === 'object' && data != 'null') {
return true;
}
return false;
}
index.js
import { Observer } from './source/Observer.js';
// 声明一个 obj,为 plain Object
const obj = {
a: {
aa: 1
},
b: 2,
}
// 将 obj 整体 reactive 化
new Observer(obj);
// 无输出
obj.b = 2;
// set b - 2 - 3 - newVal is Basic Value
obj.b = 3;
// set b - 3 - [object Object] - newVal is Object
obj.b = {
bb: 4
}
// get b-[object Object]
obj.b;
// get a-[object Object]
obj.a;
// get aa-1
obj.a.aa
// set aa - 1 - 3 - newVal is Basic Value
obj.a.aa = 3
这样,我们就完成了 Vue 的第一个核心逻辑, 成功把一个任意层级的 plain object 转化成 reactive object
2. 如何实现一个 watcher
前面讲的是如何将 plain object 转换成 reactive object. 接下来讲一下,如何实现一个watcher.
实现的伪代码应如下:
伪代码
// 传入 data 参数新建新建一个 vue 对象
const v = new Vue({
data: {
a:1,
b:2,
}
});
// watch data 里面某个 a 节点的变动了,如果变动,则执行 cb
v.$watch('a',function(){
console.info('the value of a has been changed !');
});
// watch data 里面某个 b 节点的变动了,如果变动,则执行 cb
v.$watch('b',function(){
console.info('the value of b has been changed !');
})
Vue.js
// 引入将上面中实现的 Observer
import { Observer } from './Observer.js';
import { Watcher } from './Watcher.js';
export default class Vue {
constructor(options) {
// 在 this 上挂载一个公有变量 $options ,用来暂存所有参数
this.$options = options
// 声明一个私有变量 _data ,用来暂存 data
let data = this._data = this.$options.data
// 在 this 上挂载所有 data 里的 key 值, 这些 key 值对应的 get/set 都被代理到 this._data 上对应的同名 key 值
Object.keys(data).forEach(key => this._proxy(key));
// 将 this._data 进行 reactive 化
new Observer(data, this)
}
// 对外暴露 $watch 的公有方法,可以对某个 this._data 里的 key 值创建一个 watcher 实例
$watch(expOrFn, cb) {
// 注意,每一个 watcher 的实例化都依赖于 Vue 的实例化对象, 即 this
new Watcher(this, expOrFn, cb)
}
// 将 this.keyName 的某个 key 值的 get/set 代理到 this._data.keyName 的具体实现
_proxy(key) {
var self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return self._data[key]
},
set: function proxySetter(val) {
self._data[key] = val
}
})
}
}
Watch.js
// 引入Dep.js, 是什么我们待会再说
import { Dep } from './Dep.js';
export class Watcher {
constructor(vm, expOrFn, cb) {
this.cb = cb;
this.vm = vm;
this.expOrFn = expOrFn;
// 初始化 watcher 时, vm._data[this.expOrFn] 对应的 val
this.value = this.get();
}
// 用于获取当前 vm._data 对应的 key = expOrFn 对应的 val 值
get() {
Dep.target = this;
const value = this.vm._data[this.expOrFn];
Dep.target = null;
return value;
}
// 每次 vm._data 里对应的 expOrFn, 即 key 的 setter 被触发,都会调用 watcher 里对应的 update方法
update() {
this.run();
}
run() {
// 这个 value 是 key 被 setter 调用之后的 newVal, 然后比较 this.value 和 newVal, 如果不相等,则替换 this.value 为 newVal, 并执行传入的cb.
const value = this.get();
if (value !== this.value) {
this.value = value;
this.cb.call(this.vm);
}
}
}
对于什么是 Dep, 和 Watcher 里的 update() 方法到底是在哪个时候被谁调用的,后面会说
3. 如何收集 watcher 的依赖
前面我们讲了 watcher 的大致实现,以及 Vue 代理 data 到 this 上的原理。现在我们就来梳理一下,Observer/Watcher 之间的关系,来说明它们是如何调用的.
首先, 我们要来理解一下 watcher 实例的概念。实际上 Vue 的 v-model, v-bind , {{ mustache }}, computed, watcher 等等本质上是分别对 data 里的某个 key 节点声明了一个 watcher 实例.
<input v-model="abc">
<span>{{ abc }}</span>
<p :data-key="abc"></p>
...
const v = new Vue({
data:{
abc: 111,
}
computed:{
cbd:function(){
return `${this.abc} after computed`;
}
watch:{
abc:function(val){
console.info(`${val} after watch`)
}
}
}
})
这里,Vue 一共声明了 4 个 watcher 实例来监听abc, 1个 watcher 实例来监听 cbd. 如果 abc 的值被更改,那么 4 个 abc - watcher 的实例会执行自身对应的特定回调(比如重新渲染dom,或者是打印信息等等)
不过,Vue 是如何知道,某个 key 对应了多少个 watcher, 而 key 对应的 value 发生变化后,又是如何通知到这些 watcher 来执行对应的不同的回调的呢?
实际上更深层次的逻辑是:
在 Observer阶段,会为每个 key 都创建一个 dep 实例。并且,如果该 key 被某个 watcher 实例 get, 把该 watcher 实例加入 dep 实例的队列里。如果该 key 被 set, 则通知该 key 对应的 dep 实例, 然后 dep 实例会将依次通知队列里的 watcher 实例, 让它们去执行自身的回调方法
dep 实例是收集该 key 所有 watcher 实例的地方.
watcher 实例用来监听某个 key ,如果该 key 产生变化,便会执行 watcher 实例自身的回调
相关代码如下:
Dep.js
export class Dep {
constructor() {
this.subs = [];
}
// 将 watcher 实例置入队列
addSub(sub) {
this.subs.push(sub);
}
// 通知队列里的所有 watcher 实例,告知该 key 的 对应的 val 被改变
notify() {
this.subs.forEach((sub, index, arr) => sub.update());
}
}
// Dep 类的的某个静态属性,用于指向某个特定的 watcher 实例.
Dep.target = null
observer.js
import {Dep} from './dep'
function makeItReactive(obj, key, val) {
var dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
// 收集依赖! 如果该 key 被某个 watcher 实例依赖,则将该 watcher 实例置入该 key 对应的 dep 实例里
if(Dep.target){
dep.addSub(Dep.target)
}
return val
},
set: (newVal) => {
if (newVal === val) {
return;
}
else if (isObject(newVal)) {
new Observer(newVal);
val = newVal;
// 通知 dep 实例, 该 key 被 set,让 dep 实例向所有收集到的该 key 的 watcher 实例发送通知
dep.notify()
}
else if (!isObject(newVal)) {
val = newVal;
// 通知 dep 实例, 该 key 被 set,让 dep 实例向所有收集到的该 key 的 watcher 发送通知
dep.notify()
}
}
})
}
watcher.js
import { Dep } from './Dep.js';
export class Watcher {
constructor(vm, expOrFn, cb) {
this.cb = cb;
this.vm = vm;
this.expOrFn = expOrFn;
this.value = this.get();
}
get() {
// 在实例化某个 watcher 的时候,会将Dep类的静态属性 Dep.target 指向这个 watcher 实例
Dep.target = this;
// 在这一步 this.vm._data[this.expOrFn] 调用了 data 里某个 key 的 getter, 然后 getter 判断类的静态属性 Dep.target 不为null, 而为 watcher 的实例, 从而把这个 watcher 实例添加到 这个 key 对应的 dep 实例里。 巧妙!
const value = this.vm._data[this.expOrFn];
// 重置类属性 Dep.target
Dep.target = null;
return value;
}
// 如果 data 里的某个 key 的 setter 被调用,则 key 会通知到 该 key 对应的 dep 实例, 该Dep实例, 该 dep 实例会调用所有 依赖于该 key 的 watcher 实例的 update 方法。
update() {
this.run();
}
run() {
const value = this.get();
if (value !== this.value) {
this.value = value;
// 执行 cb 回调
this.cb.call(this.vm);
}
}
}
总结:
至此, Watcher, Observer , Dep 的关系全都梳理完成。而这些也是 Vue 实现的核心逻辑之一。再来简单总结一下三者的关系,其实是一个简单的 观察-订阅 的设计模式, 简单来说就是, 观察者观察数据状态变化, 一旦数据发生变化,则会通知对应的订阅者,让订阅者执行对应的业务逻辑 。我们熟知的事件机制,就是一种典型的观察-订阅的模式
Observer, 观察者,用来观察数据源变化.
Dep, 观察者和订阅者是典型的 一对多 的关系,所以这里设计了一个依赖中心,来管理某个观察者和所有这个观察者对应的订阅者的关系, 消息调度和依赖管理都靠它。
Watcher, 订阅者,当某个观察者观察到数据发生变化的时候,这个变化经过消息调度中心,最终会传递到所有该观察者对应的订阅者身上,然后这些订阅者分别执行自身的业务回调即可
参考
Vue源码解读-滴滴FED
代码参考
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
如何爬去爬去今日头条动态数据,
网上有很多教程,我就不在啰嗦了
第一步如何分析得到存储数据的真实url
首先打开https://www.toutiao.com/,搜索街拍,会跳转https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D
你如果用传统的方式你将的得不到任何有价值的信息
这个时候你怎么办呢?
你这个时候注意查看requests url,
Request URL: https://www.toutiao.com/api/search/content/?aid=24&app_name=web_search&offset=0&format=json&keyword=街拍&autoload=true&count=20&en_qc=1&cur_tab=1&from=search_tab&pd=synthesis×tamp=1559831008973
到这里我们就找到了数据春芳的真正url了
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
后面就简单了,直接上代码
import os
import re
import json
import requests
from requests import RequestException
from requests import exceptions
from urllib.parse import urlencode
from demo01.util import buid_proxy
‘’’
抓取今日头条图片图片集
因为今天头条数据是动态,因此第一步是找到存储图片的真正url
第二步就是构造浏览器(伪浏览器),因为现在防爬网站做的很好,他会更具某项标准你是否是机器人,因此这步很重要
‘’’
proxies=buid_proxy()
def get_one_page(offset, keyword):
‘’’
获取网页html内容并返回
‘’’
params = {
‘aid’: ‘24’,
‘app_name’: ‘web_search’,
‘offset’: offset,
‘format’: ‘json’,
‘keyword’:keyword,
‘autoload’: ‘true’,
‘count’: ‘20’,
‘cur_tab’: ‘1’,
‘from’: ‘search_tab’,
‘pd’: ‘synthesis’,
‘timestamp’: ‘1559660659001’}
header = {
"User-Agen":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
"referer":"https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D",
"cookie":"tt_webid=6692573135994799624; UM_distinctid=16ace1c56988c-06f62adc4fd369-7a1437-144000-16ace1c5699a3; csrftoken=368635b7c1d736ff1889c2b70705afa9; tt_webid=6692573135994799624; WEATHER_CITY=%E5%8C%97%E4%BA%AC; s_v_web_id=152a5d87eb7690f9953388e50371f37b; CNZZDATA1259612802=1893030441-1558619693-https%253A%252F%252Flanding.toutiao.com%252F%7C1559662594; _ga=GA1.2.569135354.1559664708; _gid=GA1.2.419995265.1559664708; __tasessionId=wb39ej38m1559741348358",
}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(params)
#print(url)
try:
# 获取网页内容,返回json格式数据
response = requests.get(url, headers=header,proxies=proxies)
# 通过状态码判断是否获取成功
if response.status_code == 200:
#此处必须这样写不然会出现中文乱码
response=response.content.decode('utf-8')
html=response
return html
return None
except RequestException:
return None
def parse_one_page(html):
‘’’
解析出组图网址,并将网页中所有图集的标题及图片地址返回
‘’’
urls = []
data = json.loads(html,encoding=‘utf-8’)
if data and ‘data’ in data.keys():
for item in data.get(‘data’):
#print(item)
page_urls = []
title = item.get(‘title’)
#print(title)
image_list = item.get(‘image_list’)
if image_list !=None:
for i in range(len(image_list)):
# 获取large图片地址
url = image_list[i][‘url’]
# 替换URL获取高清原图
url = url.replace(‘large’, ‘origin’)
page_urls.append(url)
urls.append({‘title’: title,‘url_list’: page_urls})
return urls
def save_image_file(url, path):
‘’’
保存图像文件
‘’’
ir = requests.get(url)
if ir.status_code == 200:
with open(path, ‘wb’) as f:
f.write(ir.content)
f.close()
def main(offset, word):
html = get_one_page(offset, word)
#print(html)
urls = parse_one_page(html)
print(urls)
#图像文件夹不存在则创建
root_path = 'E:/test001/photo/TOUTIAO'
if not os.path.exists(root_path):
os.mkdir(root_path)
for i in range(len(urls)):
print('---正在下载 %s'%urls[i]['title'])
folder = root_path + '/' + urls[i]['title']
if not os.path.exists(folder):
try:
os.mkdir(folder)
except NotADirectoryError:
continue
except OSError:
continue
url_list = urls[i]['url_list']
try:
for j in range(len(url_list)):
path = folder + '/index_' + str("%02d"%j) + '.jpg'
if not os.path.exists(path):
save_image_file(urls[i]['url_list'][j], path)
except exceptions.ProxyError:
return None
if name == ‘main’:
main(0,‘街拍’)
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
全新的界面设计 ,将会带来全新的写作体验;
在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
全新的 KaTeX数学公式 语法;
增加了支持甘特图的mermaid语法1 功能;
增加了 多屏幕编辑 Markdown文章功能;
增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block
var foo = 'bar';
1
2
生成一个适合你的列表
项目
项目
项目
项目1
项目2
项目3
计划任务
完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 Value
电脑 $1600
手机 $12
导管 $1
设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右
第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash
创建一个自定义列表
Markdown
Text-to-HTML conversion tool
Authors
John
Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ(n)=(n−1)!∀n∈N \Gamma(n) = (n-1)!\quad\foralln\in\mathbb NΓ(n)=(n−1)!∀n∈N 是通过欧拉积分
Unexpected text node: ' 'Unexpected text node: ' '
Γ(z)=∫
0
∞
t
z−1
e
−t
dt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
Mon 06
Mon 13
Mon 20
已完成
进行中
计划一
计划二
现有任务
Adding GANTT diagram functionality to mermaid
关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
张三
李四
王五
你好!李四, 最近怎么样?
你最近怎么样,王五?
我很好,谢谢!
我很好,谢谢!
李四想了很长时间,文字太长了不适合放在一行.
打量着王五...
很好... 王五, 你怎么样?
张三
李四
王五
这将产生一个流程图。:
链接
长方形
圆
圆角长方形
菱形
关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
开始
我的操作
确认?
结束
yes
no
关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
mermaid语法说明 ↩︎
注脚的解释 ↩︎
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
Echarts数据化可视图表
开发工具与关键技术:Visual Studio 2015 使用Echarts图表
作者:郭海明
撰写时间:2019年 6月 4日
1
2
3
在创建一个网页的时候,有一些页面会有许多数据,浏览起来枯燥而乏味,而且数据之间的对比也不够明显。那么有什么方法可以让这些数据的对比明显起来,使数据看起来也没有那么枯燥呢!答案当然是有的,我们只需要引用Echarts数据可视化图表就可以了。使用Echarts数据化图表不仅可以让数据之间对比明显,还可以使数据更加生动起来,增强用户浏览页面数据的体验感。
Echarts提供了常规的折线图,柱状图,饼状图,散点图。还有用于统计的盒型图,用于地理数据可视化的地图,热力图,线图,用于关系数据可视化的关系图,多维数据可视化的平行坐标。还有用户BI的漏斗图,仪表盘,并且支持图与图之间的混搭。
Echarts图表使用起来的方法页很简单,首先将Echarts插件引用到视图里面,视图里面添加显示Echarts图表的类。给这个类显示图表的空间,并给类ID,用于后面写好图表之后将图表放到这个类里面。
然后在
将学生信息数据表格需要用到的数据表,进行多表的连接查询,连接完成之后,
获取到需要用到的数据,封装到自定义的AchievementInfor的实体类里面。
写出接收不同阶段成绩的方法。用于接收每个不同阶段的成绩.
写完之后,返回到视图里面去写调用Echarts的图表,这里我们显示的是折线图,所以首先在
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
本文介绍了两种在 JavaScript 读取文件的方法
文件读取在当今的编程语言中应该算是一项比较 trivial 的工作了,语言的标准库一般都会帮助我们做好文件抽象与读写缓存,我们仅需要熟悉和运用相关的 API 即可.
但是 JavaScript 由于安全性的原因,一直以来都没有提供相关的文件读取接口,但有时我们确实需要读取本地文件内容,下面是两种可能的读取方法.
1. 使用 HTML5 中的 FileReader
HTML5 引入的 FileReader 可以帮助我们读取本地文件,但是有一个限制,就是我们不能直接使用文件路径的方式来访问文件,而是首先需要用户选择文件(通过文件选择或者拖动文件至网页等方式)
代码还是比较简单的,首先你需要在 html 文件中添加文件选择表单:
<input type="file" id="file-input" />
<div id="file-content" />
1
2
接着就可以在 JavaScript 中进行(用户选择的)文件读取了:
function readSingleFile(e) {
// file from "file-input"
var file = e.target.files[0];
if (!file) {
return;
}
// create FileReader
var reader = new FileReader();
// load callback
reader.onload = function(e) {
var contents = e.target.result;
// process file contents here
// ...
};
// do read
reader.readAsText(file);
}
// add file input change listener
var fileInput = document.getElementById("file-input");
fileInput.addEventListener("change", readSingleFile, false);
2. 使用 XMLHttpRequest
XMLHttpRequest 一般用于实现 Ajax,通过他我们也可以实现本地文件的读取(但是有同源等限制),示例代码如下:
var contents = "";
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.status == 200 && xmlhttp.readyState == 4) {
contents = xmlhttp.responseText;
// process file contents here
// ...
}
};
// send read request
xmlhttp.open("GET","abc.txt",true);
xmlhttp.send();
更多资料
http://researchhubs.com/post/computing/javascript/open-a-local-file-with-javascript.html
https://www.html5rocks.com/en/tutorials/file/dndfiles/
https://www.liaoxuefeng.com/wiki/1022910821149312/1023022332902400
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
最近折腾自己的网站,在自适应方面发现有了很多新的方法,感叹前端的技术真是日新月异,从以前只能这样,到现在除了这样,还可以那样,甚至再那样......技术永无止境啊。
回到主题,自适应响应式布局这个话题古老而新颖,从最早的不同分辨率的屏幕,到现在遍布各个生活角落的移动设备,响应式布局已经成了几乎所有互联网公司必备的条件。在我的映像中,bootstrap一直是响应式的头号选择,的确,其中的各种xl,xs,栅格布局让响应式变得很容易。
话说,这Vue做响应式,其实一点都不复杂:一个生命周期钩子,一个条指令,一套js判断语句解决,说到这,高手们应该早已经明白如何操作了, 这篇文章就给广大刚入门的同学们拓宽一下吧(不熟悉Vue的同学,还是先恶补一下Vue基础吧)。
一个生命周期钩子——mounted:挂载时操作;一条指令——v-show(本例中采用,非绝对):根据条件显示;一套js判断语句:if/else或者switch/case。具体操作起来很简单:(代码直接展示)
<!--这是一段导航html-->
<nav id="nav-part" class="easyUtil-backImgPostion easyUtil-flexContainerRow">
<ul class="easyUtil-flexContainerRow">
<li class="nav-list" :class="nav.className" v-for="(nav,i) in navs">
<a :href="nav.href">{{nav.name}}</a>
</li>
</ul>
<div id="serach-part" v-show="show"><input type="search" id="search" :placeholder="placeholder" class="easyUtil-input"><button id="searchBtn" class="easyUtil-btn">{{btn}}</button></div>
</nav>
var head = new Vue({
el : "#head",
data : {
//此处省略一千字
show : true
},
mounted : function(){
//可用于设置自适应屏幕,根据获得的可视宽度(兼容性)判断是否显示
let w = document.documentElement.offsetWidth || document.body.offsetWidth;
if(w < 1000){
this.show = false;
}
})
我的目的是在移动设备中不显示搜索栏(search-part)部分,那么利用v-show,和mounted配合,在挂载时检测一下屏幕可视宽度,如果小于1000,则认为是手机,v-show设为false,不显示即可。
看,很简单吧,简单到我觉得自己好像在忽悠。其实到这里,原理已经说完,具体的应用大家可以自行发挥,而且也不一定就用v-show,我这里是为了显示与否,如果大家想添加样式什么的,还可以写别的,甚至于计算属性,watch都可以。只要记住在挂载的时候完成即可,不然页面会有跳动,不利于体验。
可能有人会问,用css的@media就可以完成了,为啥用js,我这里想说的是,我并没有否认@media,这里只是写出更多一种方式,同时结合一下现在很火的前端框架。多一种方法,就多一种解决思路,不至于再回到以前"只能这样"的道路上。具体到实际应用中,当然是最适合的方法为主。前端水深,前端的路子也越来越多,越来也丰富,敞开思维,就会看见不同的天空。
ps一句,js确实需要刷新页面重新加载才会显示,也就是说如果用户将页面从大屏移动到小屏幕,就可能出现布局不响应问题,但是在实际使用中,这种情况几乎不会出现,因为用户不能可能将电脑打开的网页,直接移动到手机上,操作永远都是重新打开,所以js,css在显示效果上是没有区别的(当然如果js太过复杂,性能会有影响)。另外,文中方法的兼容性问题,只要支持Vue的浏览器,都没问题。
就说到这吧,欢迎大神指正,不胜感激!
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
蓝蓝设计的小编 http://www.lanlanwork.com