Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

Vue-js入门简介

发表于 2019-07-26 | 更新于 2023-03-23

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

Vue实例生命周期图示

Vue实例生命周期图示

核心插件Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex工作原理示意图

Vuex核心store

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:

1
2
3
4
5
6
7
8
9
10
11
12
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)

const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})

现在,你可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:

1
2
3
store.commit('increment')

console.log(store.state.count) // -> 1

Vuex核心概念

State

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):

1
2
3
4
5
6
7
8
9
10
11
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})

通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。

Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 接受 state 作为其第一个参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})

Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:

1
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

1
2
3
4
5
6
7
8
9
10
11
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})

你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:

1
store.commit('increment')

注意,Mutation必须是同步函数。

Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})

Action 通过 store.dispatch 方法触发:

1
store.dispatch('increment')

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}

const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

参考链接

  1. Vue.js 教程,by Vue.js Homepage.
  2. Vuex教程,by Vue.js Homepage.
  3. vue 页面销毁函数_45. 聊聊 Vue 的生命周期,by atone2003.
  4. 使用vue-router切换组件时使组件不销毁,by 龙易安.

Lodash入门简介

发表于 2019-07-25

Lodash是一个一致性、模块化、高性能的 JavaScript 实用工具库。

安装

浏览器环境:

1
<script src="lodash.js"></script>

通过 npm:

1
2
$ npm i -g npm
$ npm i --save lodash

Node.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Load the full build.
var _ = require('lodash');
// Load the core build.
var _ = require('lodash/core');
// Load the FP build for immutable auto-curried iteratee-first data-last methods.
var fp = require('lodash/fp');

// Load method categories.
var array = require('lodash/array');
var object = require('lodash/fp/object');

// Cherry-pick methods for smaller browserify/rollup/webpack bundles.
var at = require('lodash/at');
var curryN = require('lodash/fp/curryN');

优点

Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。

Lodash 的模块化方法 非常适用于:

  • 遍历 array、object 和 string
  • 对值进行操作和检测
  • 创建符合功能的函数

示例

1
2
3
4
_.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 });
// → { 'a': 1, 'b': 2 }
_.partition([1, 2, 3, 4], n => n % 2);
// → [[1, 3], [2, 4]]

参考链接

  1. Lodash,by lodash homepage.

express-jwt使用帮助

发表于 2019-07-25 | 更新于 2019-07-31

express-jwt是一个验证JsonWebTokens并设置req.user的connect/express中间件。此模块允许您使用Node.js应用程序中的JWT令牌验证HTTP请求。 JWT通常用于保护API端点,通常使用OpenID Connect发布。

Json web token结构

JWT 的三个部分依次如下:

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

Header

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

1
2
3
4
5
6
7
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

ignature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

基于JWT的身份验证流程

使用基于 JWT 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

jsonwebtoken的使用

jsonwebtoken是JSON Web Tokens在Nodejs平台的实现。它可以与express-jwt模块配合,在expressjs中实现基于JWT的身份验证。

安装步骤

1
npm install jsonwebtoken --save

使用帮助

在expressjs中主要使用该模块生成JSON Web Tokens。其接口定义为:

  • jwt.sign(payload, secretOrPrivateKey, [options, callback])
1
2
3
4
5
6
var token = jwt.sign({
userId: users[0].id
},
appConfig.secretOrPrivateKey, {
expiresIn: 60 * 60 * 6
});
  • jwt.verify(token, secretOrPublicKey, [options, callback])
1
2
3
// verify a token symmetric - synchronous
var decoded = jwt.verify(req.body.token.split(' ')[1], appConfig.secretOrPrivateKey);
var user = await models.User.findByPk(decoded.userId);
  • jwt.decode(token [, options])
1
2
3
4
5
6
7
// get the decoded payload ignoring signature, no secretOrPrivateKey needed
var decoded = jwt.decode(token);

// get the decoded payload and header
var decoded = jwt.decode(token, {complete: true});
console.log(decoded.header);
console.log(decoded.payload)

express-jwt的使用

安装帮助

1
npm install express-jwt --save

使用示例

1
2
3
4
5
6
7
8
9
10
app.use(expressJWT({
secret: appConfig.secretOrPrivateKey
}));

app.use(function(err, req, res, next) {
console.log(err);
if (err.name === 'UnauthorizedError') {
res.status(err.status || 401).send(err.message);
}
});

参考链接

  1. 什么是 JWT – JSON WEB TOKEN, by Dearmadman
  2. JSON Web Token 入门教程,by 阮一峰.
  3. Cookie,Session和Token会话知识整理,by jack huang.
  4. node-jsonwebtoken,by auth0.

Expressjs组件morgan保存日志到文件的方法

发表于 2019-07-24 | 更新于 2019-07-25

morgan是express默认的日志中间件,也可以脱离express,作为node.js的日志组件单独使用。

示例

将日志打印到本地文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var express = require('express');
var app = express();
var morgan = require('morgan');
var fs = require('fs');
var path = require('path');

var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {flags: 'a'});

app.use(morgan('short', {stream: accessLogStream}));
app.use(function(req, res, next){
res.send('ok');
});

app.listen(3000);

日志切割

一个线上应用,如果所有的日志都落地到同一个本地文件,时间久了,文件会变得非常大,既影响性能,又不便于查看。这时候,就需要用到日志分割了。

借助file-stream-rotator插件,可以轻松完成日志分割的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var FileStreamRotator = require('file-stream-rotator')
var express = require('express')
var fs = require('fs')
var morgan = require('morgan')
var path = require('path')

var app = express()
var logDirectory = path.join(__dirname, 'log')

// ensure log directory exists
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)

// create a rotating write stream
var accessLogStream = FileStreamRotator.getStream({
date_format: 'YYYYMMDD',
filename: path.join(logDirectory, 'access-%DATE%.log'),
frequency: 'daily',
verbose: false
})

// setup the logger
app.use(morgan('combined', {stream: accessLogStream}))

app.get('/', function (req, res) {
res.send('hello, world!')
})

参考链接

  1. Node 进阶:express 默认日志组件 morgan 从入门使用到源码剖析,by 程序猿小卡.

Expressjs cors入门简介

发表于 2019-07-24

在nodejs+expressjs+vuejs的解决方案中,服务器端应用开发和前端应用的开发是独立的。不过前端应用调用服务器端应用的RESTful API时涉及跨域资源访问,因此需要添加expressjs的cors模块。下面简要介绍cors模块的使用方法。

跨域资源共享简介

跨域资源共享(CORS)是一种机制,允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。网页可以自由嵌入跨源图像,样式表,脚本,iframe和视频。但某些“跨域”请求,尤其是Ajax请求,默认情况下是由同源安全策略禁止的。

CORS定义了一种浏览器和服务器可以交互的方式,以确定允许跨源请求是否安全。它允许比纯粹的同源请求更多的自由和功能,但比简单地允许所有跨源请求更安全。 CORS规范最初是作为W3C建议书发布的,但该文件已经过时。目前主动维护的CORS定义规范是WHATWG的Fetch Living Standard。

CORS标准描述了新的HTTP标头,它为浏览器提供了一种仅在获得许可时才能请求远程URL的方法。 尽管服务器可以执行某些验证和授权,但浏览器通常负责支持这些标头并遵守它们所施加的限制。

对于可以修改数据的Ajax和HTTP请求方法(通常是GET以外的HTTP方法,或者某些MIME类型的POST用法),规范要求浏览器“预检”请求,通过HTTP OPTIONS请求从服务器请求支持的方法 方法,然后,在服务器“批准”时,使用实际的HTTP请求方法发送实际请求。 服务器还可以通知客户端是否应该随请求一起发送“凭证”(包括Cookie和HTTP身份验证数据)。

跨域资源共享流程图

图1 跨域资源共享流程图

CORS安装

CORS是一个node.js包,用于提供Connect / Express中间件,可用于启用具有各种选项的CORS。使用如下方式安装。

1
npm install cors --save

CORS示例

  • 简单示例,允许所有跨域请求
1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express')
var cors = require('cors')
var app = express()

app.use(cors())

app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})

app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})

参考链接

  1. Cross-origin resource sharing,by wikipedia.
  2. 跨域资源共享 CORS 详解,by 阮一峰.
  3. cors,by expressjs.com.

nodemon入门简介

发表于 2019-07-23

Nodemon是一个用于nodejs开发的工具程序,它将监视nodejs源中的任何更改并自动重新启动服务器。

nodemon不需要对代码或开发方法进行任何其他更改。 nodemon是node的替换包装器,在执行脚本时使用nodemon替换命令行上的单词node即可。

安装

1
npm install -g nodemon

使用

1
nodemon [your node app]

参考链接

  1. nodemon,by remy.
  2. 在 Express 开发中使用 nodemon,by bubkoo.

RESTful_API设计及测试入门

发表于 2019-07-23 | 更新于 2019-07-24

RESTful API是目前比较成熟的一套互联网应用程序的API设计理论,提供了一种统一的机制,方便不同的前端设备与后端进行通信,以应对前端设备层出不穷(手机、平板、桌面电脑、其他专用设备……)的状况。同时对开发人员而言,提供了一种规范,方便协作开发,降低了API接口设计的难度。

RESTful API设计规范

请参考阮一峰老师的RESTful API 设计指南。

RESTful API的测试方法

RESTful API设计好了之后,需要测试其可用性。常用的API测试工具有Postman。简单入门可参考Postman 安装及使用入门教程。

Postman中使用HTTP POST方法测试RESTful API时,需要了解POST提交数据的方法。

POST提交数据的方法

POST 几种请求主要包括 application/x-www-form-urlencoded、 multipart/form-data、 application/json、text/xml 四种的方法。

服务端通常是根据请求头(Request Headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。
POST 提交数据方案,包含了 Content-Type 和消息主体编码方式两部分。

  • application/x-www-form-urlencoded (默认常用)

这是最常见的 POST 提交数据的方式。浏览器的原生表单,如果不设置Content-type属性,就会默认该方式提交数据,并且提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。

  • multipart/form-data

这是另一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让表单的enctyped等于 multipart/form-data。

  • application/json

application/json 这种方案,可以方便的提交复杂的结构化数据,这种POST提交方式越来越多,用来告诉服务端消息主体是序列化后的 JSON 字符串。

由于JSON规范的流行,出了低版本的IE之外,各大浏览器都支持 JSON.stringify 将JSON数据字符串化(编码)传到服务器,还可以通过 JSON.parse 将字符串转化(解码)为JSON数据供前端渲染或者其他处理。

  • text/xml

现在基本不用。

参考链接

  1. RESTful API 设计指南,by 阮一峰.
  2. RESTful API 设计最佳实践,by Philipp Hauer.
  3. restful-api-design-references,by aisuhua.
  4. Postman Doc,by postman homepage.
  5. Postman 安装及使用入门教程,by Mafly.
  6. 四种常见的 POST 提交数据方式,by Jerry Qu.

HTML_DOM文档对象模型简介

发表于 2019-07-22 | 更新于 2023-04-01

最近要写一些八股文,上网一搜,好多范例,正想下载下来借鉴以下,可惜不让复制粘贴。不过难不倒我,直接使用文档对象模型(Document Object Model,DOM)提供编程接口,将整篇文章下来。

DOM概述

DOM简介

文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。

一个web页面是一个文档。这个文档可以在浏览器窗口或作为HTML源码显示出来。但上述两个情况中都是同一份文档。文档对象模型(DOM)提供了对同一份文档的另一种表现,存储和操作的方式。 DOM是web页面的完全的面向对象表述,它能够使用如 JavaScript等脚本语言进行修改。

DOM 和 JavaScript

开始的时候,JavaScript和DOM是交织在一起的,但它们最终演变成了两个独立的实体。JavaScript可以访问和操作存储在DOM中的内容,因此我们可以写成这个近似的等式:

1
API (web 或 XML 页面) = DOM + JS (脚本语言)

DOM 被设计成与特定编程语言相独立,使文档的结构化表述可以通过单一,一致的API获得。尽管通常使用JavaScript操作DOM, 但DOM 也可以使用其他的语言来实现。

DOM访问

在使用DOM时,您不需要做任何其他特殊的操作。不同的浏览器都有对DOM不同的实现, 这些实现对当前的DOM标准而言,都会呈现出不同程度的一致性,每个web浏览器都会使用一些文档对象模型,从而使页面可以被脚本语言访问。

当您在创建一个脚本时-无论是使用内嵌 <script>元素或者使用在web页面脚本加载的方法— 您都可以使用 document或 window 元素的API来操作文档本身或获取文档的子类(web页面中的各种元素)。

您的DOM编程代码可能会像下面例子一样非常简单,如使用 window对象的alert()函数显示一个警告信息,或者使用比较复杂的方法来创建一个新的内容,如下面内容较长的实例所示。

1
<body onload="window.alert('welcome to my home page!');">

重要的数据类型

为简单起见,在API参考文档中的语法实例通常会使用element(s) 指代节点,使用nodeList(s)或 element(s)来指代节点数组,使用 attribute(s)来指代属性节点。

数据类型 解释
document 当一个成员返回 document 对象 (例如,元素的 ownerDocument 属性返回它所属于 document ) ,这个对象就是root document 对象本身。 DOM document Reference 一章对 document 对象进行了描述。
element element 是指由 DOM API 中成员返回的类型为 element 的一个元素或节点。 例如, document.createElement() 方法会返回一个 node 的对象引用,也就是说这个方法返回了在DOM中创建的 element。 element 对象实现了 DOM Element 接口以及更基本的 Node 接口,参考文档将两者都包含在内。
nodeList nodeList 是一个元素的数组,如从 document.getElementsByTagName() 方法返回的就是这种类型。 nodeList 中的条目由通过下标有两种方式进行访问:list.item(1)和list[1]。两种方式是等价的,第一种方式中 item() 是 nodeList 对象中的单独方法。 后面的方式则使用了经典的数组语法来获取列表中的第二个条目。
attribute 当 attribute 通过成员函数 (例如,通过 createAttribute()方法) 返回时,它是一个为属性暴露出专门接口的对象引用。DOM中的属性也是节点,就像元素一样,只不过您可能会很少使用它。
namedNodeMap namedNodeMap 和数组类似,但是条目是由name或index访问的,虽然后一种方式仅仅是为了枚举方便,因为在 list 中本来就没有特定的顺序。 出于这个目的, namedNodeMap 有一个 item() 方法,你也可以从 namedNodeMap 添加或移除条目。

DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。

节点的类型有七种。

  • Document:整个文档树的顶层节点
  • DocumentType:doctype标签(比如<!DOCTYPE html>)
  • Element:网页的各种HTML标签(比如<body>、<a>等)
  • Attribute:网页元素的属性(比如class=”right”)
  • Text:标签之间或标签包含的文本
  • Comment:注释
  • DocumentFragment:文档的片段

浏览器提供一个原生的节点对象Node,上面这七种节点都继承了Node,因此具有一些共同的属性和方法。

DOM中核心接口

在DOM编程时,通常使用的最多的就是 Document 和 window 对象。简单的说, window 对象表示浏览器中的内容,而 document 对象是文档本身的根节点。Element 继承了通用的 Node 接口, 将这两个接口结合后就提供了许多方法和属性可以供单个元素使用。在处理这些元素所对应的不同类型的数据时,这些元素可能会有专用的接口,如上节中的 table 对象的例子。

下面是在web和XML页面脚本中使用DOM时,一些常用的API简要列表。

  • document.getElementById(id)
  • document.getElementsByTagName(name)
  • document.createElement(name)
  • parentNode.appendChild(node)
  • element.innerHTML
  • element.style.left
  • element.setAttribute()
  • element.getAttribute()
  • element.addEventListener()
  • window.content
  • window.onload
  • window.dump()
  • window.scrollTo()

Element的尺寸

  • scrollWidth:对象的实际内容的宽度,不包边线宽度,会随对象中内容超过可视区后而变大。
  • clientWidth:对象内容的可视区的宽度,不包滚动条等边线,会随对象显示大小的变化而改变。
  • offsetWidth:对象整体的实际宽度,包滚动条等边线,会随对象显示大小的变化而改变。

当元素内容没有超过可视区域

元素内无内容或者内容不超过可视区,滚动不出现或不可用的情况下。

scrollWidth=clientWidth,两者皆为内容可视区的宽度。

offsetWidth为元素的实际宽度。

文档下载实战

以示例范文为例,提取其中内容。

禁止复制粘贴的原理

  • 阻止默认的事件响应
1
2
3
4
5
6
7
8
9
10
11
12
<script type="text/javascript">
// 禁止右键菜单
document.oncontextmenu = function(){ return false; };
// 禁止文字选择
document.onselectstart = function(){ return false; };
// 禁止复制
document.oncopy = function(){ return false; };
// 禁止剪切
document.oncut = function(){ return false; };
// 禁止粘贴
document.onpaste = function(){ return false; };
</script>
  • CSS阻止用户选择
1
2
3
element.style {
user-select: none;
}

破解禁止复制粘贴

使用chrome打开该网页,在使用快捷键ctrl+shift+i,打开开发者工具,找到文档节点DIV的id为article,使用如下代码提取文章内容:

1
2
var node=document.getElementById('article')
console.log(node.innerText)

DOM事件机制

请参考DOM的事件机制,以及事件的捕获与冒泡。

参考链接

  1. DOM 模型概述,by 阮一峰.
  2. DOM概述,by MDN web DOC.
  3. 网页上如何实现禁止复制粘贴以及如何破解,by 小茗同学.
  4. scrollWidth,clientWidth,offsetWidth等等的区别,by 南果梨.
  5. javascript中top、clientTop、scrollTop、offsetTop的讲解,by 小豆soybean.
  6. 令人头疼的clientTop、scrollTop、offsetTop,by 凌晨风.
  7. DOM的事件机制,以及事件的捕获与冒泡,by 墨城之左.
  8. DOM事件机制以及事件委托总结,by 余小兔.

Vue-cli-3集成jQWidgets

发表于 2019-07-19

jQWidgets是一个软件框架,包含小部件,主题,输入验证,拖放插件,数据适配器,内置WAI-ARIA可访问性,国际化和MVVM支持。它建立在开放标准和技术HTML5,CSS,JavaScript和jQuery之上。该库用于开发响应式Web和移动应用程序。下面介绍在Vuejs中集成jQWidgets。

安装环境

  • node.js: v10.16.0
  • npm: 6.9.0
  • vue: 2.6.10
  • cesium: 1.45.0
  • vue-cli: 3.9.2
  • webpack: 4.28.4

安装步骤

  • 安装vue-cli
1
npm install -g @vue/cli
  • 创建应用
1
vue create jqwidgets-vue-cli-app   //Choose the default preset
  • 导航到应用
1
cd jqwidgets-vue-cli-app
  • 安装jQWidgets依赖
1
npm install jqwidgets-scripts --save--dev
  • 从node_modules/jqwidgets-scripts/jqwidgets复制styles文件夹到public文件夹。

  • 在index.html链接 jqx.base.css 文件。

1
<link rel="stylesheet" type="text/css" href="./styles/jqx.base.css" />
  • 修改App.vue如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<JqxBarGauge ref="barGaugeReference"
:width="600" :height="600"
:values="values" :formatFunction="formatFunction"
/>
</template>

<script>
import JqxBarGauge from 'jqwidgets-scripts/jqwidgets-vue/vue_jqxbargauge.vue';

export default {
components: {
JqxBarGauge
},
data: function () {
return {
values: [10, 20, 30, 40, 50]
}
},
methods: {
formatFunction: function (value, index, color) {
return color;
}
}
}
</script>
  • 运行Vue Cli开发服务器
1
npm run serve
  • 在浏览器中输入 localhost:8080 查看结果。

参考链接

  1. Vue CLI with jQWidgets,by jQWidgets.

Vue-cli 3集成Cesium

发表于 2019-07-18 | 更新于 2023-07-14

之前记录了在Vue-Cli 2的基础上集成了Cesium,但现在Vue-Cli已更新到3,之前方法已不适用。在参考Vue-cli 3.0 + cesium 构建的基础上,验证记录Vue-Cli 3脚手架工具中集成Cesium的过程。

安装环境

  • node.js: v10.16.0
  • npm: 6.9.0
  • vue: 2.6.10
  • cesium: 1.45.0
  • vue-cli: 3.9.2
  • webpack: 4.28.4

安装配置

  • 安装nodejs

  • 安装vue-cli脚手架

1
2
3
4
npm install -g @vue/cli
vue create my-project
cd my-project
npm run serve
  • 安装 cesium
1
npm i cesium --save
  • 新建 vue.config.js 文件,配置内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
const path = require('path')

const debug = process.env.NODE_ENV !== 'production'
let cesiumSource = './node_modules/cesium/Source'
let cesiumWorkers = '../Build/Cesium/Workers'
module.exports = {
baseUrl: '',
devServer: {
port: 9999
},
configureWebpack: {
output: {
sourcePrefix: ' '
},
amd: {
toUrlUndefined: true
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve('src'),
'cesium': path.resolve(__dirname, cesiumSource)
}
},
plugins: [
new CopyWebpackPlugin([ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers'}]),
new CopyWebpackPlugin([ { from: path.join(cesiumSource, 'Assets'), to: 'Assets'}]),
new CopyWebpackPlugin([ { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets'}]),
new CopyWebpackPlugin([ { from: path.join(cesiumSource, 'ThirdParty/Workers'), to: 'ThirdParty/Workers'}]),
new webpack.DefinePlugin({
CESIUM_BASE_URL: JSON.stringify('./')
})
],
module: {
unknownContextCritical: /^.\/.*$/,
unknownContextCritical: false

}
}
}
  • 在 src/components 下新建 CesiumViewer.vue 文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div id="cesiumContainer" class="fullSize">

</div>
</template>

<script>
export default {
name: "CesiumViewer"
}
</script>

<style scoped>
.fullSize {
display: block;
position: absolute;
top: 0;
left: 0;
border: none;
width: 100%;
height: 100%;
}
</style>
  • 修改 router.js ,引入 CesiumViewer组件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import Router from 'vue-router'
import CesiumViewer from './components/CesiumViewer.vue'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
name: 'CesiumViewer',
component: CesiumViewer
}
]
})
  • 修改App.vue ,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div id="app">
<router-view/>
</div>
</template>

<style lang="css">
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
  • 最后修改 main.js 内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Cesium from 'cesium/Cesium'
// noinspection ES6UnusedImports
import widget from 'cesium/Widgets/widgets.css'

Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App),
mounted () {
var viewer = new Cesium.Viewer('cesiumContainer')
}
}).$mount('#app')
  • 最后 npm serve ,打开浏览器输入localhost:8080查看效果。

注意事项

不应将viewer定义在vue组件的data对象中,因为vue会劫持数据,导致cesium卡顿。具体请参考vue+cesium cesium数据量太大导致浏览器卡顿解决办法。

参考链接

  1. Vue-cli 3.0 + cesium 构建,by QingMings.
  2. cesium-and-webpack,by marsgis.
  3. Vue.js集成Cesium,by jack huang.
  4. Cesium笔记 — Vue+Cesium开发环境搭建(基于Cesium1.69),by 芒果香蕉.
  5. vue+cesium cesium数据量太大导致浏览器卡顿解决办法,by 浮年似水.
上一页1…383940…54下一页

Jack Huang

535 日志
69 标签
© 2026 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse