玩转 CMS
玩转 CMS
目前接手的内容管理系统(CMS)基于 ant-design-vue-pro(简称模板项目
或ant-vue-pro
) 开发的,经过许多次迭代,形成了现在的模样(简称本地项目
)。
假如让一名新手接手这个项目,他会遇到很多问题,比如 .env 的作用、开发时后端接口没有写好如何联调、样式使用less还是 CSS Modules、表单和表格如何使用等等
技术是为产品服务,只需要能用技术做出项目,不需要所有技术、所有最佳实践都清楚。好比中医发展了好几千年,许多本源的东西老中医也是不清楚的,但我们摸索出一套规则,按照这个能治病,这个就很好。
本地项目使用的是 ant design vue 1.x 版本,基于 vue 2
本系列目的:让新手快速接手这个 CMS 系统
样式
Ant Design Pro 默认使用 less 作为样式语言。
Tip: less 语法 —— 重要,不紧急(后续补上)。直接在 less 中用 css 语法也能完成项目,然后逐步的利用 less 功能。
vscode 搜索,发现 90% 以上都有 scoped
,样式语言也确实是 less。
// 69处
<style
// 49处
<style lang="less" scoped>
// 15处
<style scoped>
样式开发过程,要避免全局污染
,通过 scoped 特性和 css modules 设置组件样式作用域。
<style lang="less" scoped>
.chart-trend {
display: inline-block;
font-size: 16px;
line-height: 24px;
}
</style>
Tip:
注
:避免在 scoped 中使用元素选择器。比如转成 button[data-v-xxxxxx]
会比类选择器组合要慢,因为要匹配的元素太多了。
@import
在单文件组件的样式中,通过 @import 引入 less 文件:
<template>
<div>
<p class="red">hello</p>
</div>
</template>
<style lang="less" scoped>
@import './index.less';
</style>
// index.less
.red{
color: red;
font-size: 23.5px;
}
请问 .red 是全局的还是局部的,是否会影响到其他页面?
笔者测试发现,是局部
的。最后编译出来是这样:
<style type="text/css">.red[data-v-2b80bebf] {
color: red;
font-size: 23.5px;
}
</style>
Tip:网上有的说这么写是全局。
如果不加 scoped
则会全局生效。就像这样:
<style lang="less">
@import './index.less';
</style>
导入写法
- 从 ant-design-vue 库的样式文件中导入 index.less 文件
// index.less
// ~ant-design-vue/lib/style/index 表示从 ant-design-vue 库的样式文件中导入 index 文件
// 导入的是 index.less 文件,而不是 index.css
// 在 Less 中,通过 @import 关键字导入的文件可以是 Less 文件或 CSS 文件。如果导入的文件没有指定后缀名,Less 会尝试导入同名的 .less 文件,如果不存在,Less 会尝试导入同名的 .css 文件。
@import '~ant-design-vue/lib/style/index';
- 同一目录下的名为chart.less的文件
<style lang="less" scoped>
// 同一目录下的名为chart.less的文件。不存在,Less 将会继续尝试导入同名的chart.css文件
@import "chart";
</style>
@import '../index.less';
是 Less 的新语法格式,它不使用 url() 函数。更加简洁和直观。
// 旧语法
@import url('../index.less')
// 新语法
@import '../index.less';
使用的是支持新语法的 Less 版本,这两种写法是等价的。
@import "~@/components/index.less";
是一种在 Less 中导入模块化组件的常见方式。
<style lang="less" scoped>
// 正确
@import "~@/components/test.less";
// 错误。less 并不会识别 @ 符号作为项目根目录的表示
// @import "@/components/test.less";
</style>
样式文件类别
在一个项目中,样式文件根据功能不同,可以划分为不同的类别
公共样式
可以将样式提取到一个公共文件,比如 Pro 提取的 src/global.less 然后在 main.js 将样式引入 import './global.less'
工具样式
src/utils/utils.less 这里可以放置一些工具函数供调用,比如清除浮动 .clearfix。
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
height: 0;
clear: both;
font-size: 0;
visibility: hidden;
}
}
.clearfix() 混合器定义了清除浮动的样式。然后,你可以通过 .clearfix() 在选择器 .selector 中调用混合器,从而应用清除浮动的样式:
.selector {
// 调用 .clearfix() 混合器
.clearfix();
}
通用模块级
例如 src/layouts/BasicLayout.less,里面包含一些基本布局的样式,被 src/layouts/BasicLayout.vue 引用,项目中使用这种布局的页面就不需要再关心整体布局的设置。如果你的项目中需要使用其他布局,也建议将布局相关的 js 和 less 放在这里 src/layouts
。
组件级
组件相关的样式,有一些在页面中重复使用的片段或相对独立的功能,你可以提炼成组件,相关的样式也应该提炼出来放在组件中,而不是混淆在页面里。
Tip:有时样式配置特别简单,也没有重复使用,你也可以用内联样式 style="{ fontSize: fontSizeVar }" 来设置。
覆盖组件样式
由于业务的个性化需求,我们经常会遇到需要覆盖组件样式的情况。请看示例:
<template>
<div class="test-wrapper">
<a-select v-model="name" style="width:400px">
<a-select-option value="1">Option 1</a-select-option>
<a-select-option value="2">Option 2</a-select-option>
<a-select-option value="3">Option 3</a-select-option>
</a-select>
</div>
</template>
<script>
export default {
data(){
return {
name: 'Option 1'
}
}
}
</script>
<style lang="less" scoped>
// 使用 scss, less 时,可以用 /deep/ 进行样式穿透
.test-wrapper ::v-deep .ant-select {
font-size: 26px;
}
.test-wrapper /deep/ .ant-select {
font-weight: 700;
}
</style>
<style scoped>
/* 这里注释不可以用 `//` */
.test-wrapper >>> .ant-select {
color: blue
}
</style>
在 scss、less 中可以使用 /deep/
或::v-deep
进行样式穿透,在css 中可以使用 >>>
穿透。
最终渲染成:
<style type="text/css">
.test-wrapper[data-v-2b80bebf] .ant-select {
font-size: 26px;
}
.test-wrapper[data-v-2b80bebf] .ant-select {
font-weight: 700;
}
</style>
<style type="text/css">
/* 这里注释不可以用 `//` */
.test-wrapper[data-v-2b80bebf] .ant-select {
color: blue
}
</style>
请求
axios
首先回顾下 axios 如何使用的。
在 vue-admin-template(基于 element-ui) 中使用 axios 有以下几步(参考这里):
- 安装 axios 包
- 对 axios 进行封装,比如封装到 request.js 文件中。关键增加请求拦截器和响应拦截器,比如返回 403、500等都会通过 Message 组件提示给用户
- 每个页面(或模块)引入 request.js,导出接口。例如 api/table.js
Tip: 以前我们研究的 spug 开源项目(基于react)中 axios 也是类似用法 —— react axios
ant-vue-pro 中axios 用法类似:
- 通过 src\utils\request.js 封装 request.js
- 每个页面(或模块)引入 request.js,导出接口。例如登录模块对应
src\api\login.js
为了方便管理维护,统一的请求处理都放在 @/src/api
文件夹中,并且一般按照 model 纬度进行拆分文件,如:
api/
user.js
permission.js
goods.js
...
本地项目 api
本地项目的 api 大概是这样:
import { axios } from '@/utils/request'
import cancelAxios from 'axios'
import qs from 'qs'
/* 取消请求 */
var CancelToken = cancelAxios.CancelToken
export let cancellistApi
// 列表
export function list (parameter) {
return axios({
url: '/acms/demo/list',
method: 'get',
// params 参数用于将数据通过查询字符串的形式添加到请求的 URL 中。这种方式适用于 GET 请求
params: parameter,
cancelToken: new CancelToken(function (c) {
cancellistApi = c
}),
// paramsSerializer 是 axios 的一个配置选项,用于将请求参数序列化为 URL 查询字符串格式
// 比如转换开始结束时间的格式:rangeDate[]=2023-11-11&rangeDate[]=2023-12-03 转成 rangeDate=2023-11-11&rangeDate=2023-12-03
paramsSerializer: function (params) {
return qs.stringify(params, {
arrayFormat: 'repeat'
})
}
})
}
// get请求
export function review (id) {
return axios({
url: `/acms/demo/detail/${id}`,
method: 'get'
})
}
// post请求
export function pass (data) {
return axios({
url: `/acms/demo/pass`,
method: 'post',
// data 参数则是将数据作为请求的正文发送给服务器。这种方式适用于 POST、PUT、DELETE 等请求
// 请求中的 Content-Type 头附带的是 application/json 或 multipart/form-data 等适合传递数据的类型
data
})
}
// 删除文章
export function delArticle (id) {
return axios({
url: `/acms/article/${id}`,
// DELETE 方法用于请求服务器删除指定的资源。它通常需要在请求中指定要删除的资源的标识符。例如,使用 DELETE 方法可以删除用户账号、删除文章等。
method: 'delete'
})
}
// 上线文章
// PUT 方法用于向指定的 URL 发送数据,通常是用于更新服务器上的资源
export function onlineArticle (id) {
return axios({
url: `/acms/article/online/${id}`,
method: 'put'
})
}
get、post、put、delete请求,有时引入 qs
包,用于将请求的参数对象序列化,比如处理开始时间和结束时间。
Tip: qs
是一个用于序列化和反序列化 URL 查询字符串的 JavaScript 库。比如:
- 序列化:将 JavaScript 对象序列化为 URL 查询字符串的格式,以便作为请求参数添加到 URL 中。例如,将 { key1: 'value1', key2: 'value2' } 转换为 key1=value1&key2=value2。
- 反序列化:将 URL 查询字符串解析为 JavaScript 对象,方便进行参数的提取和处理。例如,将 key1=value1&key2=value2 转换为 { key1: 'value1', key2: 'value2' }。
- 处理复杂参数:qs 支持处理复杂对象、数组等数据结构,可以将它们转换为合适的 URL 查询字符串格式,方便进行网络请求。
cancelAxios 用于取消请求。不过有的同事用法不对,他用在搜索的 input 框中,想实现输入字符延迟查询。可以用 .lazy 或 lodash 的延迟。
Tip: ant design vue 中 lazy(<a-input v-model.lazy=
)不起作用。根据场景可以使用lodash 中的防抖或节流。当调用 delayedRequest 函数时,如果在 1000 毫秒内没有再次调用该函数,那么延迟时间结束后,请求逻辑将会执行。
import { debounce } from 'lodash';
const delayedRequest = debounce(() => {
// 在这里执行你的请求逻辑
}, 1000); // 延迟时间为 1000 毫秒
// 调用 delayedRequest 函数
delayedRequest();
async和Promise
Tip:有关 promise 和 async 的介绍请看笔者之前文章:Promise、async
在 ant-vue-pro 中只使用了 Promise,没有使用 async
。
从 ./src/views 中搜索:
- async、await 都没有
- promise 在13个文件中有 23 处。
用法大致如下:
// 模拟网络请求、卡顿 800ms
new Promise((resolve) => {
}).then(() => {
})
// 两个都成功才进入 then
Promise.all([repositoryForm, taskForm]).then(values => {
}).catch(() => {
})
new Promise((resolve) => {
}).then(() => {
}).catch(() => {
// 总是会执行。比如关闭`加载中...`弹框
}).finally(() => {
})
笔者认为 async 也需要用起来,async 和 Promise 不是替代关系,各有其使用场景 —— 使用 promise 还是 async/await
本地项目的写法
本地项目
的写法有以下几种:
- 取消发布。只处理了成功的情况
// axios 在响应拦截器中已经处理了http 非 200 的请求,也处理的 5000、4000 等 token 过期或其他错误,最后到这里通常是约定好的接口数据。
cancelPublish(params).then((res) => {
if (res.code === 0) {
this.getDataList()
this.$message.success(res.msg)
}
})
- 编辑。在 finally 中关闭关闭
加载中...
弹框
async editFn (contentType, params) {
this.loading = true
try {
const res = await updateArticle(params)
if (res.code === 0) {
// 请求成功...
}
} catch (err) {
} finally {
this.loading = false
}
},
- 只有一个异步请求,并且需要处理错误情况。可以这么写:
fetchData(false).then(() => {
// do ...
}).catch(() => {
console.log('error')
})
如果不觉得 try...catch 麻烦,也可以这样:
try {
let p = await fetchData(false)
// do ...
} catch (e) {
console.log('error')
}
注
:try...catch 除了可以捕获语法报错,还能捕获 reject 。
const fetchData = new Promise((resolve, reject) => {
reject(11);
});
async function myAsyncFunction() {
try {
let p = await fetchData;
console.log('p', p);
// 其他代码...
} catch (e) {
console.log('error', e);
}
}
myAsyncFunction();
// => error 11
需要注意的几点
错误写法
遮罩层没有消失
async function request() {
console.log('开启遮罩')
// 报错就退出了
let json = await requestUserList() // {1}
// 处理数据...
console.log('关闭遮罩');
}
await 与并行
// 下面这段代码是串行
async function foo() {
let a = await createPromise(1)
let b = await createPromise(2)
}
可以通过下面两种方法改为并行:
// 方式一
async function foo() {
let p1 = createPromise(1)
let p2 = createPromise(2)
// 至此,两个异步操作都已经发出
await p1
await p2
}
// 方式二
async function foo() {
let [p1, p2] = await Promise.all([createPromise(1), createPromise(2)])
}
async 有时会比 Promise 更容易调试
promise.catch
以下两段代码等效
promise1.then(null, () => {
console.log('拒绝')
})
// 等价于
promise1.catch(() => {
console.log('拒绝')
})
- 链式捕获错误
let p1 = new Promise((resolve, reject) => {
resolve('10') // {1}
})
// 三个完成处理程序都有可能出错,我们可以在末尾添加一个已拒绝处理的程序对这个链式统一处理
p1.then(() => {
throw new Error('fail')
console.log(1)
}).then(() => {
console.log(2)
}).then(() => {
console.log(3)
}).catch(e => {
console.log(e.message)
})
// 输出:fail
如果将 {1} 改成 reject(10),也会直接到 catch 中,这时 e 就是 10。
await 的返回值
await 命令后面是一个 Promise 对象。如果不是,会被转为一个立即 resolve 的 Promise 对象。Promise 的解决值会被当作该 await 表达式的返回值。
async function fa() {
return await 1
}
// 等价于
async function fa() {
return await Promise.resolve(1)
}
// 等价于
async function foo() {
return await new Promise((resolve, reject) => {
resolve(1)
})
}
在看一个示例:
function resolveAfter2Seconds(x) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
let x = await resolveAfter2Seconds(10).then(res => {return 1});
console.log(x); // 1
}
f1();
每调用一次 then 就会创建一个新的 Promise。
async 函数中的 return
return 返回值,会成为 then() 方法回调函数的参数
async function foo() {
return 'hello'
}
foo().then(v => {
console.log(v)
})
// hello
Promise 执行器错误
每个执行器中都隐含一个 try-catch 块,所以错误会被捕获并传入给已拒绝回调。以下两段代码等价:
let p1 = new Promise(function(resolve, reject){
throw new Error('fail')
})
p1.catch(v => {
console.log(v.message) // fail
})
let p1 = new Promise(function(resolve, reject){
try{
throw new Error('fail')
}catch(e){
reject(e)
}
})
...
env
.env 是一种用来存储环境变量的文件。
模板项目的 env
在 ant-vue-pro 中一共有三个 .env 文件。
// .env
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_API_BASE_URL=/api
// .env.development
NODE_ENV=development
VUE_APP_PREVIEW=true
VUE_APP_API_BASE_URL=/api
// .env.preview
NODE_ENV=production
VUE_APP_PREVIEW=true
VUE_APP_API_BASE_URL=/api
.env
- 在所有的环境中被载入
.env.[mode]
- 只在指定的模式中被载入
一个环境文件只包含环境变量的“键=值”对:
FOO=bar
VUE_APP_NOT_SECRET_CODE=some_value
FOO2 = bar # 等号前后加空格也可以
注
:只有 NODE_ENV
,BASE_URL
和以 VUE_APP_
开头的变量默认可以被识别。比如 FOO=bar
就不会被识别,除非使用其他手段进行变量扩展。
三个模式
默认情况下,一个 Vue CLI 项目有三个模式:
- development 模式用于 vue-cli-service serve
- test 模式用于 vue-cli-service test:unit
- production 模式用于 vue-cli-service build 和 vue-cli-service test:e2e
比如配置如下:
// .env
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_API_BASE_URL=/api
VUE_APP_address = 长沙
// .env.development
VUE_APP_PREVIEW=true
VUE_APP_API_BASE_URL=/api
tel = 2222
VUE_APP_NAME=peng 3
VUE_APP_tel = 1111
运行 npm run serve
(对应package.json中 "serve": "vue-cli-service serve",
),会依次加载 .env 和 .env.development,后者会将前者的值覆盖,所以最后通过 process.env 输出:
console.log(process.env)
{
BASE_URL: "/"
NODE_ENV: "development"
VUE_APP_API_BASE_URL: "/api"
VUE_APP_NAME: "peng 3"
VUE_APP_PREVIEW: "true"
VUE_APP_address: "长沙"
VUE_APP_tel: "1111"
}
比如:
- VUE_APP_address 从 .env 中得到
- tel 被忽略
- NODE_ENV 在 .env.development 中被自动加上该属性
- VUE_APP_tel 中 = 前后有空格也能生效
Tip:每次修改 .env,需要重新启动服务才会生效。
--mode
可以通过 --mode 覆写默认的模式。比如本地开发我可以代理到测试的url,也像代理到预发布的url,我可以这样做:
增加 .env.pre:
VUE_APP_URL=/myapi
package.json 增加:
"scripts": {
"serve": "vue-cli-service serve",
+ "serve:pre": "vue-cli-service serve --mode pre",
通过 npm run serve:pre 就能操作预发布环境的数据。现在输出:
console.log(process.env)
{
BASE_URL: "/"
NODE_ENV: "development"
VUE_APP_API_BASE_URL: "/api"
VUE_APP_PREVIEW: "false"
VUE_APP_URL: "/myapi"
VUE_APP_address: "长沙"
}
注意,现在 NODE_ENV 是 development,这个值来自 .env,vue-cli 没有给我们增加一个 NODE_ENV 的变量。
execSync
关于构建,有的人可能会通过配置一个 js 去执行,这样能更灵活,比如运维需要你创建一个每次创建一个文件。就像这样:
// package.json
"scripts": {
"build": "node src/libs/shell.js",
"build:pre": "node src/libs/shell.js pre",
"build:test": "node src/libs/shell.js test"
// shell.js
var dist = 'dist'
var d = new Date().getTime().toString()
var env = process.argv.splice(2)[0]
var writeFileSync = require('fs').writeFileSync
var execSync = require('child_process').execSync
if (env == 'test') {
execSync('vue-cli-service build --mode test')
} else if (env == 'pre') {
execSync('vue-cli-service build --mode pre')
} else {
execSync('vue-cli-service build --mode prod')
}
writeFileSync(dist + '/xx.txt', d)
Tip: execSync 是 Node.js 的 child_process 模块提供的一个同步执行外部命令的函数。它允许通过 JavaScript 代码来执行系统命令,并等待命令执行完成后再继续执行后续代码。
Mock
ant-vue-pro 使用的是 mockjs2(好像和 mockjs 是同一个东西)。
Tip: mockjs 不会在浏览器中看到请求发出,更多有关 mockjs 的使用方法,请看 这里。
本地项目使用的 proxy,当后端没有提供接口给前端时,前端还是需要自己去模拟数据。
笔者通过如下方式给模板项目添加 mockjs。
首先模板项目中有 ant-vue-pro 中的 mockjs2 包,直接跳过安装包。
创建 src/mock/index.js:
// 判断环境不是 prod 时加载 mock 服务
if (process.env.NODE_ENV !== 'production') {
console.log('[antd-pro] mock mounting')
const Mock = require('mockjs2')
require('./skin.js')
Mock.setup({
timeout: 100 // 设置所有请求的响应时间为100ms
})
}
// skin.js
import Mock from 'mockjs2'
const navList = {
"code": 0,
"msg": "查询成功",
"error": "",
"url": null,
"data": [
{...},
],
"success": true
};
Mock.mock(/\/mockjs-cms\/channel\/list\/navigation/, 'get', navList)
main.js 中引入 src/mock/index:
import './mock/index.js'
最后是发起请求:
export function queryNavigation() {
return axios({
url: `/mockjs-cms/channel/list/navigation`,
method: 'get',
})
}
注:中途笔者遇到两个问题:
- mockjs 返回了导航数据,但页面没有显示导航。将
timeout: 800
改成timeout: 100
。 - 本地开发时,保存
mock/index.js
(按 ctrl + s) 文件 vscode 不会触发自动编译,在别的文件中保存会触发自动编译。最后引入这个资源,比如在 main.js 中引入,然后重启服务或vscode即可实现保存自动编译。
另外公司使用了 eolink,后端定义好接口后,就有一个简易 Mock 链接
,开发阶段前端可以这样:
export function queryNavigation() {
return axios({
// 简易 Mock 链接
url: 'https://mockapi.eolink.com/81F5kJv4c4f5ff6d3b8a7880xxxx',
method: 'get',
})
}
玩转 CMS的更多相关文章
- PHPCMS V9 如何启用伪静态
最近在研究CMS时候,首先是使用DEDECMS,后来又转到了PHPCMS,感觉后者架构更加合理,而前者主要是模板众多,故使用者多一些,不过我都是需要自己写模板,那就无所谓了. 玩各种CMS我喜欢首先看 ...
- 如何玩转小程序+公众号?手把手教你JeeWx小程序CMS与公众号关联
随着微信小程序新功能.新入口的不断更新,小程序的商业价值逐步增强,特别是小程序与公众号的深度融合,已经让小程序成为各行业新的营销渠道.Jeewx平台专注小程序的开发,逐步完善小程序生态圈,通过简单操作 ...
- 玩转 .NET Core 3.0:逐浪CMS新版发布,建站更简单、网站更安全
2019年11月11日,在大家都忙于网上体会“双11 ”的热闹气氛的时候,逐浪CMS开发者团队正在做着新版本发布的最后工作.此次更新是基本于 .NET Core 3.0开发,也是全国首个基于 .NET ...
- 织梦cms PHPcms 帝国cms比较
现在建网站不需要请程序员从基础的程序开发做起了,有专业的建站工具,CMS是使用最广泛的建站工具.CMS是Content Management System 现在建网站不需要请程序员从基础的程序开发做起 ...
- CMS垃圾回收机制
详解CMS垃圾回收机制 原创不易,未经允许,不得转载~~~ 什么是CMS? Concurrent Mark Sweep. 看名字就知道,CMS是一款并发.使用标记-清除算法的gc. CMS是针对老 ...
- 详解CMS垃圾回收机制
原创不易,未经允许,不得转载~~~ 什么是CMS? Concurrent Mark Sweep. 看名字就知道,CMS是一款并发.使用标记-清除算法的gc. CMS是针对老年代进行回收的GC. CMS ...
- [orleans2.1]这是你没玩过的船新版本
不知不觉orleans就发布到2.1版本的,但是说也奇怪orleans越是完善我发现园子相关的博客就越少,大概是大佬都在美滋滋用在生产环境,不屑于玩demo了吧. 但是小弟不才还是只会玩demo,所以 ...
- .NET Core实战项目之CMS 第四章 入门篇-Git的快速入门及实战演练
写在前面 上篇文章我带着大家通过分析了一遍ASP.NET Core的源码了解了它的启动过程,然后又带着大家熟悉了一遍配置文件的加载方式,最后引出了依赖注入以及控制反转的概念!如果大家把前面几张都理解了 ...
- .NET Core实战项目之CMS 第六章 入门篇-Vue的快速入门及其使用
写在前面 上面文章我给大家介绍了Dapper这个ORM框架的简单使用,大伙会用了嘛!本来今天这篇文章是要讲Vue的快速入门的,原因是想在后面的文章中使用Vue进行这个CMS系统的后台管理界面的实现.但 ...
- .NET Core实战项目之CMS 第十章 设计篇-系统开发框架设计
这两天比较忙,周末也在加班,所以更新的就慢了一点,不过没关系,今天我们就进行千呼万唤的系统开发框架的设计.不知道上篇关于架构设计的文章大家有没有阅读,如果阅读后相信一定对架构设计有了更近一部的理解,如 ...
随机推荐
- 完美:C# Blazor中显示Markdown并添加代码高亮
昨天发了一篇介绍这个库:C# Blazor中显示Markdown文件,介绍怎么在Blazor中显示Markdown内容的文章,文章内的代码是没有高亮的,思来相去,还是要做好,于是百度到这篇文章.NET ...
- js - 使用 scroll属性手撸轮播图 —— 无缝连接,更丝滑
上效果图: 上代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 【ES系列】(一)简介与安装
首发博客地址 首发博客地址 系列文章地址 教学视频 为什么要学习 ES? 强大的全文搜索和检索功能:Elasticsearch 是一个开源的分布式搜索和分析引擎,使用倒排索引和分布式计算等技术,提供了 ...
- [转帖]TiDB 5.1 Write Stalls 应急文档
https://tidb.net/blog/ac7174dd#4.%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E5%87%BA%E7%8E%B0%E4%BA%86%20w ...
- [转帖]python读取配置文件获取所有键值对_python总结——处理配置文件(ConfigParser)
python处理ConfigParser 使用ConfigParser模块读写ini文件 (转载) ConfigParserPython 的ConfigParser Module中定义了3个类对INI ...
- 使用shell进行简单分析增量更新时间的方法
使用shell进行简单分析增量更新时间的方法 思路 产品里面更新增量时耗时较久, 想着能够简单分析下哪些补丁更新时间久 哪些相同前缀的补丁更新的时间累积较久. 本来想通过全shell的方式进行处理 但 ...
- 基于华为fusionstorage的块存储CSI
承接上文,块存储的CSI要比对象存储复杂一些,但总的处理逻辑还是一致的.下面以华为fusionstorage的CSI为例进行介绍,该插件支持了多个后端存储,如fusionstorage和oceanst ...
- Promise练习文件读取
1. fs读取文件 const fs=require('fs');//引入文件读取模块 fs.readFile('./README.md',(err,data)=>{ // 如果出现错误,抛出错 ...
- 【主流技术】浅析 ElasticSearch7.x 的基本结构及应用(一)
目录 前言 一.概述 1.1基本认识 1.2核心概念 对比关系型数据库 1.3倒排索引 例一: 例二: 1.4了解ELK 二.安装(基于 CentOS) 2.1安装声明 2.2 使用 Docker 安 ...
- 知识图谱项目实战(一):瑞金医院MMC人工智能辅助构建知识图谱--初赛实体识别【1】
1.技术背景&赛题介绍: A Labeled Chinese Dataset for Diabetes中文糖尿病标注数据集详情请见. 数据集链接:瑞金医院MMC人工智能辅助构建知识数据源:知识 ...