為什么要自己搭建項(xiàng)目 ?
這個(gè)事情起始于前兩天給團(tuán)隊(duì)寫(xiě)一個(gè)腳手架工具,這段時(shí)間剛做做完一個(gè)系統(tǒng),是團(tuán)隊(duì)第一個(gè)正式意義上的全棧項(xiàng)目;團(tuán)隊(duì)有自己的前端腳手架,但是沒(méi)有后端腳手架,所以想著給團(tuán)隊(duì)寫(xiě)一個(gè)后端腳手架工具,這樣團(tuán)隊(duì)在開(kāi)展后續(xù)的全棧項(xiàng)目時(shí)可以使用腳手架構(gòu)建項(xiàng)目,避免了重復(fù)搭建項(xiàng)目初始架構(gòu)的大量工作,成員直接編寫(xiě)業(yè)務(wù)代碼即可。
前兩天用 JavaScript 完成了腳手架的初版,開(kāi)發(fā)過(guò)程中發(fā)現(xiàn)沒(méi)一點(diǎn)代碼提示,很不舒服,而且這樣的代碼不利于后續(xù)的迭代和維護(hù)。
所以決定用 typescript 重構(gòu)一遍,但是官方好像沒(méi)有提供一個(gè)合適的腳手架工具,于是就開(kāi)始自己搭建 typescript 項(xiàng)目;自己搭建最大的好處就是 自主可控,項(xiàng)目中集成了實(shí)時(shí)編譯的開(kāi)發(fā)環(huán)境、eslint + prettier 保證代碼質(zhì)量和風(fēng)格統(tǒng)一、項(xiàng)目構(gòu)建工具、git 提交信息的規(guī)范化,這些都是一個(gè)項(xiàng)目最基本和必要的配置。
本來(lái)到這里就結(jié)束了,但是在后續(xù)重構(gòu)腳手架的過(guò)程中發(fā)現(xiàn)一個(gè)問(wèn)題,如果每寫(xiě)一個(gè)新的 typescript 項(xiàng)目就重復(fù)一遍這個(gè)搭建流程,比如:今天需要開(kāi)發(fā)一個(gè) npm 包,明天需要開(kāi)發(fā)另外一個(gè)腳手架,這好像有點(diǎn)太麻煩了,于是就把整個(gè)搭建過(guò)程寫(xiě)成了一個(gè)腳手架,這樣在后續(xù)開(kāi)發(fā)的工作中就可以實(shí)現(xiàn)一鍵創(chuàng)建項(xiàng)目,簡(jiǎn)單、方便、舒爽
從這篇文章中你可以學(xué)到什么 ?
mkdir ts-project && cd ts-project && npm init -y && npm i typescript -D && npx tsc --init
這條命令的意思是在當(dāng)前目錄下創(chuàng)建一個(gè) ts-project 目錄,然后進(jìn)入 ts-project 目錄執(zhí)行 npm init -y 初始話目錄產(chǎn)生 package.json 文件,之后運(yùn)行 npm i typescript -D 在開(kāi)發(fā)環(huán)境安裝 typescript 包,之后執(zhí)行 npx tsc --init 生成 tsconfig.json 文件
之后所有的操作都以 ts-project 為根目錄
mkdir src && touch src/index.ts
新建 src 目錄作為項(xiàng)目的源碼目錄(開(kāi)發(fā)目錄),并在 src 目錄下創(chuàng)建 index.ts 文件作為項(xiàng)目的入口文件
如果一個(gè)目錄下存在 tsconfig.json 文件,那就意味著這個(gè)目錄是 typescirpt 項(xiàng)目的根目錄,tsconfig.json 文件中指定了用來(lái)編譯項(xiàng)目的根文件和編譯選項(xiàng),使用 tsc --init 生成的 tsconfig.json 文件包含了大量的選項(xiàng),其中大部分都被注釋掉了,一般我們只需要配置如下內(nèi)容即可:
{
"compileOnSave": true,
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":true,
"noImplicitThis": true,
"noUnusedLocals": true,
"stripInternal": true,
"pretty": true,
"declaration": true,
"outDir": "lib",
"baseUrl": "./",
"paths": {
"*": ["src/*"]
}
},
"exclude": [
"lib",
"node_modules"
]
}
npm i @types/node -D
這個(gè)是 node.js 的類型定義包
npm i ts-node-dev -D
在 package.json 的 scripts 中增加如下內(nèi)容
{
"scripts": {
"dev:comment": "啟動(dòng)開(kāi)發(fā)環(huán)境",
"dev": "ts-node-dev --respawn --transpile-only src/index.ts"
}
}
執(zhí)行 npm run dev 即可啟動(dòng)開(kāi)發(fā)環(huán)境,并且修改文件時(shí)可實(shí)時(shí)編譯
代碼質(zhì)量對(duì)于一個(gè)系統(tǒng)的可維護(hù)性、可迭代性至關(guān)重要,特別是在多人協(xié)作一個(gè)大型項(xiàng)目中,如果沒(méi)有把控代碼質(zhì)量的工具,每人一套編碼風(fēng)格,這樣的系統(tǒng)在后期的維護(hù)難度可想而知,基本上會(huì)成為一個(gè)難以迭代升級(jí)的祖?zhèn)飨到y(tǒng),除了重寫(xiě)別無(wú)他法。
因此控制代碼質(zhì)量的工具應(yīng)運(yùn)而生,而 ESLint 當(dāng)屬其中的佼佼者,熬死了各路的競(jìng)爭(zhēng)者;typescript 之前還在使用 TSLint,但在 2019 年 1 月 官方?jīng)Q定全面采用 ESLint 作為代碼檢查工具。
采用社區(qū)的開(kāi)源配置方案 eslint-config-standard,簡(jiǎn)單直接,足以 hold 住大部分項(xiàng)目了
npx eslint --init
以上流程走完以后在項(xiàng)目根目錄會(huì)多出來(lái)一個(gè) .eslintrc.js 文件,接下來(lái)在 package.json 的 scripts 中增加如下配置
{
"scripts": {
"eslint:comment": "使用 ESLint 檢查并自動(dòng)修復(fù) src 目錄下所有擴(kuò)展名為 .ts 的文件",
"eslint": "eslint --fix src --ext .ts --max-warnings=0"
}
}
Perttier 是一個(gè)專注于統(tǒng)一代碼格式風(fēng)格的工具,可能有人會(huì)疑惑,ESLint 已經(jīng)能夠規(guī)范我們的代碼,為什么還需要 Prettier ?簡(jiǎn)單來(lái)說(shuō)是這樣的,ESLint 其實(shí)有兩種類型規(guī)則:
其中 格式規(guī)則 主要是控制代碼風(fēng)格,簡(jiǎn)單理解就是代碼看起來(lái)好看、易讀,而 質(zhì)量規(guī)則 主要是發(fā)現(xiàn)代碼中存在的潛在 bug 或者可能會(huì)制造 bug 的地方,簡(jiǎn)單來(lái)說(shuō)更多是從語(yǔ)法層面去考慮,比如現(xiàn)在禁止使用 var 聲明變量,而 prettier 則是專注于 格式規(guī)則,所以在格式方面我們選用更加專業(yè)的 Prettier。
如果你同時(shí)使用 ESLint 和 Prettier,且在編輯器中配置了 Sava Auto Fix 時(shí),會(huì)讓你的一鍵格式化異常痛苦,因?yàn)樵?格式規(guī)則 上有沖突,所以個(gè)人建議或者說(shuō)不喜歡在編輯器中配置 ESLint 和 Prettier,三個(gè)原因:
接下來(lái)就開(kāi)始安裝和配置 Prettier
npm i prettier -D
安裝 Prettier 所需的依賴,然后在項(xiàng)目目錄增加 .prettierrc.js,推薦配置如下:
module.exports = {
// 一行最多 80 字符
printWidth: 80,
// 使用 2 個(gè)空格縮進(jìn)
tabWidth: 2,
// 不使用 tab 縮進(jìn),而使用空格
useTabs: false,
// 行尾需要有分號(hào)
semi: true,
// 使用單引號(hào)代替雙引號(hào)
singleQuote: true,
// 對(duì)象的 key 僅在必要時(shí)用引號(hào)
quoteProps: 'as-needed',
// jsx 不使用單引號(hào),而使用雙引號(hào)
jsxSingleQuote: false,
// 末尾使用逗號(hào)
trailingComma: 'all',
// 大括號(hào)內(nèi)的首尾需要空格 { foo: bar }
bracketSpacing: true,
// jsx 標(biāo)簽的反尖括號(hào)需要換行
jsxBracketSameLine: false,
// 箭頭函數(shù),只有一個(gè)參數(shù)的時(shí)候,也需要括號(hào)
arrowParens: 'always',
// 每個(gè)文件格式化的范圍是文件的全部?jī)?nèi)容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要寫(xiě)文件開(kāi)頭的 @prettier
requirePragma: false,
// 不需要自動(dòng)在文件開(kāi)頭插入 @prettier
insertPragma: false,
// 使用默認(rèn)的折行標(biāo)準(zhǔn)
proseWrap: 'preserve',
// 根據(jù)顯示樣式?jīng)Q定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 換行符使用 lf
endOfLine: 'lf'
}
在 package.json 的 scripts 中補(bǔ)充如下內(nèi)容
{
"scripts": {
"prettier:comment": "自動(dòng)格式化 src 目錄下的所有 .ts 文件",
"prettier": "prettier --write \"src/**/*.ts\""
}
}
如果想在編輯器中配置 ESLint 和 Prettier,具體怎么配 查看 這里,通過(guò) eslint-config-prettier 來(lái)解決沖突問(wèn)題,其作用就是關(guān)閉 ESLint 中的格式規(guī)則,只使用 Prettier 的格式規(guī)則
在系統(tǒng)開(kāi)發(fā)中,如果 git 提交說(shuō)明精準(zhǔn),在后期的協(xié)作以及 bug 處理時(shí)會(huì)變的有據(jù)可查,變相的提高了系統(tǒng)的可維護(hù)性,而且可以根據(jù)規(guī)范的提交說(shuō)明快速生成開(kāi)發(fā)日志,從而方便開(kāi)發(fā)者或用戶追蹤項(xiàng)目的開(kāi)發(fā)信息和功能特性。commitizen 是一個(gè)實(shí)現(xiàn)規(guī)范提交說(shuō)明的工具。
使用 commitizen 在項(xiàng)目中生成符合 AngularJS 規(guī)范的提交說(shuō)明,并初始化 cz-conventional-changelog 適配器
npx commitizen init cz-conventional-changelog --save --save-exact
初始化時(shí)主要做了三件事:
內(nèi)容如下:
{
"devDependencies": {
"cz-conventional-changelog": "^3.3.0"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
接下來(lái)安裝校驗(yàn)工具,負(fù)責(zé)校驗(yàn)提交信息是否符合規(guī)范
npm i @commitlint/cli @commitlint/config-conventional -D
在項(xiàng)目根目錄下新建 commitlint.config.js 并設(shè)置校驗(yàn)規(guī)則
module.exports = {
extends: ['@commitlint/config-conventional']
};
然后在 package.json 的 scripts 中增加如下內(nèi)容
{
"scripts": {
"commit:comment": "引導(dǎo)設(shè)置規(guī)范化的提交信息",
"commit": "cz"
}
}
接下來(lái),就只能使用規(guī)范化的提交信息了,如果不知道規(guī)范是什么,可在 git add . 之后執(zhí)行 npm run commit 代替 git commit,會(huì)彈出一個(gè)列表,引導(dǎo)你一步步的填充符合規(guī)范的提交信息,熟練以后亦可用 git commit
注意 以下內(nèi)容為第 4 版 husky 的使用方式。到目前為止,如果你的項(xiàng)目還沒(méi)有執(zhí)行過(guò) git init,即項(xiàng)目沒(méi)有被 git 管理,則一定要先執(zhí)行 git init 然后再往后進(jìn)行,否則后面你需要重新安裝一遍 husky
npm i husky@4 lint-staged -D
在 package.json 中添加如下內(nèi)容
{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.ts": ["npm run eslint", "npm run prettier"]
}
}
之前設(shè)置的 ESLint、Prettier 以及 commitizen 到目前為止都只限于開(kāi)發(fā)者手動(dòng)執(zhí)行 npm run xx 才能生效,這可不行,因?yàn)檫@一點(diǎn)都不智能,而且萬(wàn)一開(kāi)發(fā)者忘執(zhí)行命令了怎么辦 ?
這時(shí)候就需要 husky 和 lint-staged 出場(chǎng)了,上述配置的原理其實(shí)就是監(jiān)聽(tīng)了 git hook 腳本的執(zhí)行,在特定的命令執(zhí)行前(pre-commit) 執(zhí)行相應(yīng)的命令(lint-staged)。
注意 這部分為 husky@5 的使用方式,和第 4 版不一樣,如果還按照第 4 版的方式使用 husky@5 有問(wèn)題,網(wǎng)上的解決方案也不可行
接下來(lái)使用 git commit -m "message" 就會(huì)看到 hook 生效了。
因?yàn)檫@個(gè)項(xiàng)目主要用于開(kāi)發(fā)一些簡(jiǎn)單的 typescript 項(xiàng)目,比如項(xiàng)目組需要封裝自己的 npm 包,所以就沒(méi)有集成第三方的構(gòu)建工具,直接用 typescript 自己的構(gòu)建能力即可,簡(jiǎn)單易用,沒(méi)有學(xué)習(xí)成本
在 package.json 中添加如下內(nèi)容
{
"scripts": {
"build:comment": "構(gòu)建",
"build": "npm run eslint && npm run prettier && rm -rf lib && tsc --build",
}
}
好了,到這里項(xiàng)目就搭建好了,雖然還有一些可優(yōu)化和擴(kuò)展的地方,但是用于開(kāi)發(fā)一個(gè)簡(jiǎn)單的 npm 包或者腳手架的項(xiàng)目來(lái)說(shuō)已經(jīng)足夠了,如有需要可以基于以上內(nèi)容再進(jìn)行進(jìn)一步的補(bǔ)充和擴(kuò)展,希望大家能從里面得到自己需要的內(nèi)容。
接下來(lái)就將上面的整個(gè)搭建過(guò)程封裝成一個(gè)腳手架,腳手架的開(kāi)發(fā)就在上面搭建的項(xiàng)目中進(jìn)行
開(kāi)發(fā)一個(gè)腳手架,一般都需要一些工具包的支持,項(xiàng)目中使用到了以下工具包:
上面列出的這些基本上就是目前開(kāi)發(fā)一款腳手架常用的工具,接下來(lái)安裝項(xiàng)目需要用到的工具包:
npm i commander chalk shelljs inquirer clear-console -S
在項(xiàng)目根目錄下的 pacakge.json 中增加如下內(nèi)容:
{
"bin": {
"ts-cli": "./bin/ts-cli.js"
}
}
bin 表示命令(ts-cli)的可執(zhí)行文件的位置,接下來(lái)在項(xiàng)目根目錄執(zhí)行 npm link,將 package.json 中的屬性 bin 的值路徑添加全局鏈接,在命令行中執(zhí)行 ts-cli 就會(huì)執(zhí)行 ./bin/ts-cli.js 文件
當(dāng)用戶安裝帶有 bin 字段的包時(shí),如果是全局安裝,npm 將會(huì)使用符號(hào)鏈接把這些文件鏈接到/usr/local/node_modules/.bin/(即全局的 node_modules/.bin 中);如果是本地安裝,會(huì)鏈接到./node_modules/.bin/。
開(kāi)發(fā)結(jié)束可執(zhí)行 npm unlink ts-cli 去掉 ts-cli 的鏈接,如果不幸你執(zhí)行 npm link 命令之后你改變了你的目錄名,在 unlink 時(shí)會(huì)無(wú)效,只能手動(dòng)去全局的 node_modules 中刪除對(duì)應(yīng)的軟連接
在項(xiàng)目根目錄下添加 bin 目錄,然后在 bin 目錄下新建 ts-cli.js,文件內(nèi)容如下:
#!/usr/bin/env node
// 將構(gòu)建目錄(lib)下的 index.js 作為腳手架的入口
require('../lib/index')
接下來(lái)正式進(jìn)入開(kāi)發(fā)階段
這是源碼的一個(gè)目錄結(jié)構(gòu)
import { program } from 'commander';
import create from './order/create';
// ts-cli -v、ts-cli --version
// 臨時(shí)禁用規(guī)則,保證這里可以通過(guò) require 方法獲取 package.json 中的版本號(hào)
/* eslint-disable @typescript-eslint/no-var-requires */
program
.version(`${require('../package.json').version}`, '-v --version')
.usage('<command> [options]');
// ts-cli create newPro
program
.command('create <app-name>')
.description('Create new project from => ts-cli create yourProjectName')
.action(async (name: string) => {
// 創(chuàng)建命令具體做的事情都在這里,name 是你指定的 newPro
await create(name);
});
program.parse(process.argv);
/**
* create 命令的具體任務(wù)
*/
import {
changePackageInfo,
end,
initProjectDir,
installDevEnviroment,
installFeature,
installTSAndInit,
installTypesNode,
isFileExist,
selectFeature,
} from '../utils/create';
// create 命令
export default async function create(projecrName: string): Promise<void> {
// 判斷文件是否已經(jīng)存在
isFileExist(projecrName);
// 選擇需要的功能
const feature = await selectFeature();
// 初始化項(xiàng)目目錄
initProjectDir(projecrName);
// 改寫(xiě)項(xiàng)目的 package.json 基本信息,比如 name、description
changePackageInfo(projecrName);
// 安裝 typescript 并初始化
installTSAndInit();
// 安裝 @types/node
installTypesNode();
// 安裝開(kāi)發(fā)環(huán)境,支持實(shí)時(shí)編譯
installDevEnviroment();
// 安裝 feature
installFeature(feature);
// 結(jié)束
end(projecrName);
}
/**
* create 命令需要用到的所有方法
*/
import {
getProjectPath,
PackageJSON,
JSON,
printMsg,
readJsonFile,
writeJsonFile,
clearConsole,
} from '../utils/common';
import { existsSync } from 'fs';
import { prompt } from 'inquirer';
import { blue, cyan, gray, red, yellow } from 'chalk';
import * as shell from 'shelljs';
import * as installFeatureMethod from './installFeature';
/**
* 驗(yàn)證當(dāng)前目錄下是否已經(jīng)存在指定文件,如果存在則退出進(jìn)行
* @param filename 文件名
*/
export function isFileExist(filename: string): void {
// 文件路徑
const file = getProjectPath(filename);
// 驗(yàn)證文件是否已經(jīng)存在,存在則推出進(jìn)程
if (existsSync(file)) {
printMsg(red(`${file} 已經(jīng)存在`));
process.exit(1);
}
}
/**
* 交互式命令行,讓用戶自己選擇需要的功能
* return ['ESLint', 'Prettier', 'CZ']
*/
export async function selectFeature(): Promise<Array<string>> {
// 清空命令行
clearConsole();
// 輸出信息
/* eslint-disable @typescript-eslint/no-var-requires */
printMsg(blue(`TS CLI v${require('../../package.json').version}`));
printMsg('Start initializing the project:');
printMsg('');
// 選擇功能,這里配合 下面的 installFeature 方法 和 ./installFeature.ts 文件為腳手架提供了良好的擴(kuò)展機(jī)制
// 將來(lái)擴(kuò)展其它功能只需要在 choices 數(shù)組中增加配置項(xiàng),然后在 ./installFeature.ts 文件中增加相應(yīng)的安裝方法即可
const { feature } = await prompt([
{
name: 'feature',
type: 'checkbox',
message: 'Check the features needed for your project',
choices: [
{ name: 'ESLint', value: 'ESLint' },
{ name: 'Prettier', value: 'Prettier' },
{ name: 'CZ', value: 'CZ' },
],
},
]);
return feature as Array<string>;
}
/**
* 初始化項(xiàng)目目錄
*/
export function initProjectDir(projectName: string): void {
shell.exec(`mkdir ${projectName}`);
shell.cd(projectName);
shell.exec('npm init -y');
}
/**
* 改寫(xiě)項(xiàng)目中 package.json 的 name、description
*/
export function changePackageInfo(projectName: string): void {
const packageJSON: PackageJSON = readJsonFile<PackageJSON>('./package.json');
packageJSON.name = packageJSON.description = projectName;
writeJsonFile<PackageJSON>('./package.json', packageJSON);
}
/**
* 安裝 typescript 并初始化
*/
export function installTSAndInit(): void {
// 安裝 typescript 并執(zhí)行命令 tsc --init 生成 tsconfig.json
shell.exec('npm i typescript -D && npx tsc --init');
// 覆寫(xiě) tsconfig.json
const tsconfigJson: JSON = {
compileOnSave: true,
compilerOptions: {
target: 'ES2018',
module: 'commonjs',
moduleResolution: 'node',
experimentalDecorators: true,
emitDecoratorMetadata: true,
inlineSourceMap: true,
noImplicitThis: true,
noUnusedLocals: true,
stripInternal: true,
pretty: true,
declaration: true,
outDir: 'lib',
baseUrl: './',
paths: {
'*': ['src/*'],
},
},
exclude: ['lib', 'node_modules'],
};
writeJsonFile<JSON>('./tsconfig.json', tsconfigJson);
// 創(chuàng)建 src 目錄和 /src/index.ts
shell.exec('mkdir src && touch src/index.ts');
}
/**
* 安裝 @types/node
* 這是 node.js 的類型定義包
*/
export function installTypesNode(): void {
shell.exec('npm i @types/node -D');
}
/**
* 安裝開(kāi)發(fā)環(huán)境,支持實(shí)時(shí)編譯
*/
export function installDevEnviroment(): void {
shell.exec('npm i ts-node-dev -D');
/**
* 在 package.json 的 scripts 中增加如下內(nèi)容
* "dev:comment": "啟動(dòng)開(kāi)發(fā)環(huán)境",
* "dev": "ts-node-dev --respawn --transpile-only src/index.ts"
*/
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson.scripts['dev:comment'] = '啟動(dòng)開(kāi)發(fā)環(huán)境';
packageJson.scripts['dev'] =
'ts-node-dev --respawn --transpile-only src/index.ts';
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 安裝用戶選擇的功能
* @param feature 功能列表
*/
export function installFeature(feature: Array<string>): void {
feature.forEach((item) => {
const func = (installFeatureMethod[
`install${item}`
] as unknown) as () => void;
func();
});
// 安裝 husky 和 lint-staged
installHusky(feature);
// 安裝構(gòu)建工具
installFeatureMethod.installBuild(feature);
}
/**
* 安裝 husky 和 lint-staged,并根據(jù)功能設(shè)置相關(guān)命令
* @param feature 用戶選擇的功能列表
*/
function installHusky(feature: Array<string>): void {
// feature 副本
const featureBak = JSON.parse(JSON.stringify(feature));
// 設(shè)置 hook
const hooks = {};
// 判斷用戶是否選擇了 CZ,有則設(shè)置 hooks
if (featureBak.includes('CZ')) {
hooks['commit-msg'] = 'commitlint -E HUSKY_GIT_PARAMS';
}
// 設(shè)置 lintStaged
const lintStaged: Array<string> = [];
if (featureBak.includes('ESLint')) {
lintStaged.push('eslint');
}
if (featureBak.includes('Prettier')) {
lintStaged.push('prettier');
}
installFeatureMethod.installHusky(hooks, lintStaged);
}
/**
* 整個(gè)項(xiàng)目安裝結(jié)束,給用戶提示信息
*/
export function end(projectName: string): void {
printMsg(`Successfully created project ${yellow(projectName)}`);
printMsg('Get started with the following commands:');
printMsg('');
printMsg(`${gray('$')} ${cyan('cd ' + projectName)}`);
printMsg(`${gray('$')} ${cyan('npm run dev')}`);
printMsg('');
}
/**
* 實(shí)現(xiàn)各個(gè)功能的安裝方法
*/
import * as shell from 'shelljs';
import { writeFileSync } from 'fs';
import { PackageJSON, printMsg, readJsonFile, writeJsonFile } from './common';
import { red } from 'chalk';
/**
* 安裝 ESLint
*/
export function installESLint(): void {
shell.exec(
'npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D',
);
// 添加 .eslintrc.js
const eslintrc = `module.exports = {
"env": {
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
}
};
`;
try {
writeFileSync('./.eslintrc.js', eslintrc, { encoding: 'utf-8' });
} catch (err) {
printMsg(`${red('Failed to write .eslintrc.js file content')}`);
printMsg(`${red('Please add the following content in .eslintrc.js')}`);
printMsg(`${red(eslintrc)}`);
}
// 改寫(xiě) package.json
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson.scripts['eslint:comment'] =
'使用 ESLint 檢查并自動(dòng)修復(fù) src 目錄下所有擴(kuò)展名為 .ts 的文件';
packageJson.scripts['eslint'] = 'eslint --fix src --ext .ts --max-warnings=0';
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 安裝 Prettier
*/
export function installPrettier(): void {
shell.exec('npm i prettier -D');
// 添加 .prettierrc.js
const prettierrc = `module.exports = {
// 一行最多 80 字符
printWidth: 80,
// 使用 2 個(gè)空格縮進(jìn)
tabWidth: 2,
// 不使用 tab 縮進(jìn),而使用空格
useTabs: false,
// 行尾需要有分號(hào)
semi: true,
// 使用單引號(hào)代替雙引號(hào)
singleQuote: true,
// 對(duì)象的 key 僅在必要時(shí)用引號(hào)
quoteProps: 'as-needed',
// jsx 不使用單引號(hào),而使用雙引號(hào)
jsxSingleQuote: false,
// 末尾使用逗號(hào)
trailingComma: 'all',
// 大括號(hào)內(nèi)的首尾需要空格 { foo: bar }
bracketSpacing: true,
// jsx 標(biāo)簽的反尖括號(hào)需要換行
jsxBracketSameLine: false,
// 箭頭函數(shù),只有一個(gè)參數(shù)的時(shí)候,也需要括號(hào)
arrowParens: 'always',
// 每個(gè)文件格式化的范圍是文件的全部?jī)?nèi)容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要寫(xiě)文件開(kāi)頭的 @prettier
requirePragma: false,
// 不需要自動(dòng)在文件開(kāi)頭插入 @prettier
insertPragma: false,
// 使用默認(rèn)的折行標(biāo)準(zhǔn)
proseWrap: 'preserve',
// 根據(jù)顯示樣式?jīng)Q定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 換行符使用 lf
endOfLine: 'lf'
};
`;
try {
writeFileSync('./.prettierrc.js', prettierrc, { encoding: 'utf-8' });
} catch (err) {
printMsg(`${red('Failed to write .prettierrc.js file content')}`);
printMsg(`${red('Please add the following content in .prettierrc.js')}`);
printMsg(`${red(prettierrc)}`);
}
// 改寫(xiě) package.json
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson.scripts['prettier:comment'] =
'自動(dòng)格式化 src 目錄下的所有 .ts 文件';
packageJson.scripts['prettier'] = 'prettier --write "src/**/*.ts"';
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 安裝 CZ,規(guī)范 git 提交信息
*/
export function installCZ(): void {
shell.exec(
'npx commitizen init cz-conventional-changelog --save --save-exact',
);
shell.exec('npm i @commitlint/cli @commitlint/config-conventional -D');
// 添加 commitlint.config.js
const commitlint = `module.exports = {
extends: ['@commitlint/config-conventional']
};
`;
try {
writeFileSync('./commitlint.config.js', commitlint, { encoding: 'utf-8' });
} catch (err) {
printMsg(`${red('Failed to write commitlint.config.js file content')}`);
printMsg(
`${red('Please add the following content in commitlint.config.js')}`,
);
printMsg(`${red(commitlint)}`);
}
// 改寫(xiě) package.json
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson.scripts['commit:comment'] = '引導(dǎo)設(shè)置規(guī)范化的提交信息';
packageJson.scripts['commit'] = 'cz';
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 安裝 husky 和 lint-staged,以實(shí)現(xiàn) git commit 時(shí)自動(dòng)化校驗(yàn)
* @param hooks,需要自動(dòng)執(zhí)行的鉤子
* @param lintStaged,需要鉤子運(yùn)行的命令
*/
export function installHusky(
hooks: { [key: string]: string },
lintStaged: Array<string>,
): void {
// 初始化 git 倉(cāng)庫(kù)
shell.exec('git init');
// 在安裝 husky 和 lint-staged
shell.exec('npm i husky lint-staged -D');
// 設(shè)置 package.json
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson['husky'] = {
hooks: {
'pre-commit': 'lint-staged',
...hooks,
},
};
packageJson['lint-staged'] = {
'*.ts': lintStaged.map((item) => `npm run ${item}`),
};
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 安裝構(gòu)建工具,目前主要用于小項(xiàng)目,所以使用 typescript 原生的構(gòu)建功能即可
*/
export function installBuild(feature: Array<string>): void {
// 設(shè)置 package.json
const packageJson = readJsonFile<PackageJSON>('./package.json');
packageJson.scripts['build:comment'] = '構(gòu)建';
let order = '';
if (feature.includes('ESLint')) {
order += 'npm run eslint';
}
if (feature.includes('Prettier')) {
order += ' && npm run prettier';
}
order += ' && rm -rf lib && tsc --build';
packageJson.scripts['build'] = order;
writeJsonFile<PackageJSON>('./package.json', packageJson);
}
/**
* 放一些通用的工具方法
*/
import { readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';
import * as clear from 'clear-console';
export interface PackageJSON {
name: string;
version: string;
description: string;
scripts: {
[key: string]: string;
};
}
export interface JSON {
[key: string]: unknown;
}
/**
* 讀取指定路徑下 json 文件
* @param filename json 文件的路徑
*/
export function readJsonFile<T>(filename: string): T {
return JSON.parse(readFileSync(filename, { encoding: 'utf-8', flag: 'r' }));
}
/**
* 覆寫(xiě)指定路徑下的 json 文件
* @param filename json 文件的路徑
* @param content json 內(nèi)容
*/
export function writeJsonFile<T>(filename: string, content: T): void {
writeFileSync(filename, JSON.stringify(content, null, 2));
}
/**
* 獲取項(xiàng)目絕對(duì)路徑
* @param projectName 項(xiàng)目名
*/
export function getProjectPath(projectName: string): string {
return resolve(process.cwd(), projectName);
}
/**
* 打印信息
* @param msg 信息
*/
export function printMsg(msg: string): void {
console.log(msg);
}
/**
* 清空命令行
*/
export function clearConsole(): void {
clear();
}
執(zhí)行 npm run build 進(jìn)行構(gòu)建,構(gòu)建時(shí)會(huì)進(jìn)行代碼質(zhì)量和風(fēng)格的檢查,有些問(wèn)題可以自動(dòng)修復(fù),有些不行,不行的按照提示手動(dòng)修復(fù)一下即可,然后重新構(gòu)建
構(gòu)建完成以后找個(gè)測(cè)試目錄,執(zhí)行 ts-cli -v 或者 ts-cli --version 查看腳手架的版本
執(zhí)行 ts-cli create test 可創(chuàng)建一個(gè)名為 test 的 typescript 項(xiàng)目
修改 package.json 中的如下內(nèi)容
{
"name": "@liyongning/ts-cli"
"main": "./lib/index.js",
"keywords": ["typescript", "cli", "typescript 腳手架", "ts 腳手架", "ts-cli", "腳手架"],
"author": "李永寧",
"files": ["package.json", "README.md", "lib"],
"repository": {
"type": "git",
"url": "https://github.com/liyongning/ts-cli.git"
},
}
npm 的賬戶名、密碼就不用說(shuō),必不可少
在項(xiàng)目根目錄下增加一個(gè)發(fā)布腳本 publish.sh
#!/bin/bash
echo '開(kāi)始構(gòu)建腳手架'
npm run build
echo '腳手架構(gòu)建完成,現(xiàn)在發(fā)布'
npm publish --access public
接下來(lái)開(kāi)始發(fā)布,在項(xiàng)目根目錄下一次運(yùn)行如下命令:
npm login
根據(jù)提示依次輸入相關(guān)信息,然后執(zhí)行下面的命令完成發(fā)布
sh publish.sh
登陸 npm 查看
好了,到這里項(xiàng)目就搭建和腳手架的封裝就徹底結(jié)束了,雖然還有一些可優(yōu)化和擴(kuò)展的地方,但是對(duì)于開(kāi)發(fā)一個(gè)簡(jiǎn)單的庫(kù)或者腳手架的項(xiàng)目來(lái)說(shuō)這個(gè)腳手架已經(jīng)完全夠用了
如有需要也可以自行去擴(kuò)展,因?yàn)槟_手架內(nèi)置了不錯(cuò)的擴(kuò)展性,不論是為已有的 create 命令增加新的功能,還是新增一個(gè)命令,都很簡(jiǎn)單,方便你根據(jù)自身需要進(jìn)行二次開(kāi)發(fā)
上一篇:php基礎(chǔ)知識(shí)
主要參考:https://www.w3school.com.cn
思維導(dǎo)圖
思維導(dǎo)圖第一版
web網(wǎng)站可以說(shuō)是互聯(lián)網(wǎng)的基礎(chǔ)。每個(gè)網(wǎng)站,可以比喻為一座座房子。寬帶網(wǎng)絡(luò),就是房子門(mén)前的路。url地址,就是房子的門(mén)牌標(biāo)志。HTML代碼,就是建造房子的建筑材料(磚頭、水泥、鋼筋);CSS代碼,就是裝修房子的裝修材料;那么Javascript代碼就是這房子的水電了? JS代碼則更像是未來(lái)世界可以讓房子成為變形金剛的智能機(jī)器。因此,一些展示“老房子”的瀏覽器,可能并不支持Javascript。
定義:
HTML(Hyper Text Markup Language),是使用標(biāo)記標(biāo)簽來(lái)描述網(wǎng)頁(yè)的一種超文本標(biāo)記語(yǔ)言。
Web 瀏覽器的作用是讀取 HTML 文檔,并以網(wǎng)頁(yè)的形式顯示出它們。瀏覽器不會(huì)顯示 HTML 標(biāo)簽,而是使用標(biāo)簽來(lái)解釋頁(yè)面的內(nèi)容。HTML定義網(wǎng)頁(yè)的內(nèi)容。
CSS(Cascading Style Sheets),指層疊樣式表。樣式定義如何顯示HTML元素,規(guī)定網(wǎng)頁(yè)的布局。
Javascript 則是屬于HTML和Web的編程語(yǔ)言,對(duì)網(wǎng)頁(yè)進(jìn)行編程。
Jquery 是一個(gè)Javascript函數(shù)庫(kù)
參考上一篇:php基礎(chǔ)知識(shí),安裝-集成環(huán)境與編輯器
推薦使用 phpstudy + phpstorm
操作步驟:1、在phpstudy 安裝目錄下,把代碼文件放大到根目錄www/ 下。
2、瀏覽器直接訪問(wèn) localhost/index.html即可看到效果。
HTML元素:是從開(kāi)始標(biāo)簽(start tag)到結(jié)束標(biāo)簽(end tag)的所有代碼。
例如:<p>前面這個(gè)是開(kāi)始標(biāo)簽,中間文字是元素內(nèi)容,后面這個(gè)是結(jié)束標(biāo)簽</p>
HTML 標(biāo)簽可以擁有屬性。屬性提供了有關(guān) HTML 元素的更多的信息。
屬性總是以名稱/值對(duì)的形式出現(xiàn),比如:name="value"。
屬性總是在 HTML 元素的開(kāi)始標(biāo)簽中規(guī)定。
常用HTML元素屬性:
class :規(guī)定元素的類名(classname),一個(gè)html文件里面多個(gè)標(biāo)簽可以擁有相同的類名。
id :規(guī)定元素的唯一 id,一個(gè)html文件里面id不能相同。
style :規(guī)定元素的行內(nèi)樣式(inline style)
1、標(biāo)題:標(biāo)題(Heading)是通過(guò) <h1> - <h6> 等標(biāo)簽進(jìn)行定義的。<h1> 定義最大的標(biāo)題。<h6> 定義最小的標(biāo)題。
2、段落:通過(guò) <p> 標(biāo)簽定義。
3、注釋標(biāo)簽 <!-- 與 --> 用于在 HTML 插入注釋。
4、鏈接:<a href="http://www.yummuu.com/">www.yummuu.com</a> 。href 屬性規(guī)定鏈接的目標(biāo)。開(kāi)始標(biāo)簽和結(jié)束標(biāo)簽之間的文字被作為超級(jí)鏈接來(lái)顯示。
5、圖像:<img src="yummuu.png" alt="Yummuu" /> 。src 圖像源屬性,alt替換文本屬性。
6、表格標(biāo)簽:
7、列表標(biāo)簽
8、塊級(jí)元素和內(nèi)聯(lián)元素
<div> 元素是塊級(jí)元素,它是可用于組合其他 HTML 元素的容器。
<div> 元素沒(méi)有特定的含義。除此之外,由于它屬于塊級(jí)元素,瀏覽器會(huì)在其前后顯示折行。如果與 CSS 一同使用,<div> 元素可用于對(duì)大的內(nèi)容塊設(shè)置樣式屬性。
<div> 元素的另一個(gè)常見(jiàn)的用途是文檔布局。它取代了使用表格定義布局的老式方法。使用 <table> 元素進(jìn)行文檔布局不是表格的正確用法。<table> 元素的作用是顯示表格化的數(shù)據(jù)。
<span> 元素是內(nèi)聯(lián)元素,可用作文本的容器。
<span> 元素也沒(méi)有特定的含義。
當(dāng)與 CSS 一同使用時(shí),<span> 元素可用于為部分文本設(shè)置樣式屬性。
兩者的區(qū)別:就是在顯示時(shí)是否起新行。塊級(jí)元素會(huì)起新行,而內(nèi)聯(lián)元素則不會(huì)。
9、框架與內(nèi)聯(lián)框架:frame,<iframe src=" " name=" "></iframe>
10、腳本:<script> 定義客戶端腳本,如Javascript;<noscript> 為不支持客戶端腳本的瀏覽器定義替代內(nèi)容。
11、頭部元素:
<head> 元素是所有頭部元素的容器。<head> 內(nèi)的元素可包含腳本,指示瀏覽器在何處可以找到樣式表,提供元信息,等等。
以下標(biāo)簽都可以添加到 head 部分:<title>、<base>、<link>、<meta>、<script> 以及 <style>。
<title>:在所有 HTML/XHTML 文檔中都是必需的。它能夠定義瀏覽器工具欄中的標(biāo)題,提供頁(yè)面被添加到收藏夾時(shí)顯示的標(biāo)題,顯示在搜索引擎結(jié)果中的頁(yè)面標(biāo)題。
<base>:為頁(yè)面上的所有鏈接規(guī)定默認(rèn)地址或默認(rèn)目標(biāo)(target)
<link> :定義文檔與外部資源之間的關(guān)系。最常用于連接樣式表。
<style>:用于為 HTML 文檔定義樣式信息。
<meta> 標(biāo)簽提供關(guān)于 HTML 文檔的元數(shù)據(jù)。元數(shù)據(jù)不會(huì)顯示在頁(yè)面上,但是對(duì)于機(jī)器是可讀的。典型的情況是,meta 元素被用于規(guī)定頁(yè)面的描述、關(guān)鍵詞、文檔的作者、最后修改時(shí)間以及其他元數(shù)據(jù)。
<meta> 標(biāo)簽始終位于 head 元素中。元數(shù)據(jù)可用于瀏覽器(如何顯示內(nèi)容或重新加載頁(yè)面),搜索引擎(關(guān)鍵詞),或其他 web 服務(wù)。
<script> 標(biāo)簽用于定義客戶端腳本,比如 JavaScript。
12、HTML實(shí)體
在 HTML 中不能使用小于號(hào)(<)和大于號(hào)(>),這是因?yàn)闉g覽器會(huì)誤認(rèn)為它們是標(biāo)簽。如果希望正確地顯示預(yù)留字符,我們必須在 HTML 源代碼中使用字符實(shí)體(character entities)。
13、表單元素:
<form> :定義 HTML 表單。
<input> :是最重要的表單元素。<input> 元素有很多形態(tài),根據(jù)不同的 type 屬性。
input的輸入類型type有text、password、submit、radio、checkbox、button;(HTML5新增)number、date、color、range、month、week、time、datetime、datetime-local、email、search、tel、url。
input的常用屬性:value、readonly、disabled、size、maxlength;(HTML5新增)required、multiple、pattern、min和max、list、height和width、autocomplete
<select> :定義下拉列表 <option> 元素定義待選擇的選項(xiàng)。列表通常會(huì)把首個(gè)選項(xiàng)顯示為被選選項(xiàng)。您能夠通過(guò)添加 selected 屬性來(lái)定義預(yù)定義選項(xiàng)。
<textarea>:定義多行輸入字段(文本域)
<button>:定義可點(diǎn)擊的按鈕
樣式表允許以多種方式規(guī)定樣式信息。樣式可以規(guī)定在單個(gè)的 HTML 元素中,在 HTML 頁(yè)的頭元素中,或在一個(gè)外部的 CSS 文件中。甚至可以在同一個(gè) HTML 文檔內(nèi)部引用多個(gè)外部樣式表。
層疊次序
當(dāng)同一個(gè) HTML 元素被不止一個(gè)樣式定義時(shí),會(huì)使用哪個(gè)樣式呢?
一般而言,所有的樣式會(huì)根據(jù)下面的規(guī)則層疊于一個(gè)新的虛擬樣式表中,其中數(shù)字 4 擁有最高的優(yōu)先權(quán)。
1、瀏覽器缺省設(shè)置
2、外部樣式表
3、內(nèi)部樣式表(位于 <head> 標(biāo)簽內(nèi)部)
4、內(nèi)聯(lián)樣式(在 HTML 元素內(nèi)部)
因此,內(nèi)聯(lián)樣式(在 HTML 元素內(nèi)部)擁有最高的優(yōu)先權(quán),這意味著它將優(yōu)先于以下的樣式聲明:<head> 標(biāo)簽中的樣式聲明,外部樣式表中的樣式聲明,或者瀏覽器中的樣式聲明(缺省值)。
CSS 規(guī)則由兩個(gè)主要的部分構(gòu)成:選擇器,以及一條或多條聲明。
selector {declaration1; declaration2; ... declarationN }
每條聲明由一個(gè)屬性和一個(gè)值組成。
屬性(property)是您希望設(shè)置的樣式屬性(style attribute)。每個(gè)屬性有一個(gè)值。屬性和值被冒號(hào)分開(kāi)。
selector {property: value}
1、派生選擇器:
通過(guò)依據(jù)元素在其位置的上下文關(guān)系來(lái)定義樣式,例如: h1 span{color:red;}
2、id選擇器:
id 選擇器可以為標(biāo)有特定 id 的 HTML 元素指定特定的樣式。id 選擇器以 "#" 來(lái)定義。
3、類選擇器:
以一個(gè)點(diǎn)號(hào)顯示,例如: .className{text-align: center;}
4、屬性選擇器:
對(duì)帶有指定屬性的 HTML 元素設(shè)置樣式。例如: div[rel=’mm’]{ color:’#000’;}
可以為擁有指定屬性的 HTML 元素設(shè)置樣式,而不僅限于 class 和 id 屬性。
注釋:只有在規(guī)定了 !DOCTYPE 時(shí),IE7 和 IE8 才支持屬性選擇器。在 IE6 及更低的版本中,不支持屬性選擇。
5、后代選擇器(包含選擇器):可以選擇作為某元素后代的元素
6、子元素選擇器:選擇作為某元素子元素的元素。例如:h1>span{font-size:16px;}
7、相鄰兄弟選擇器:可選擇緊接在另一元素后的元素,且二者有相同父元素。
例如:h1 + p {margin-top:50px;}
8、偽類:用于向某些選擇器添加特殊的效果。
:active 向被激活的元素添加樣式
:focus 向擁有鍵盤(pán)輸入焦點(diǎn)的元素添加樣式
:hover 當(dāng)鼠標(biāo)懸浮在元素上方時(shí),向元素添加樣式
:link 向未被訪問(wèn)的鏈接添加樣式
:visited 向已被訪問(wèn)的鏈接添加樣式
:first-child 向元素的第一個(gè)子元素添加樣式(不建議使用)
:lang 向帶有指定lang屬性的元素添加樣式
9、偽元素:用于向某些選擇器設(shè)置特殊效果。
:first-letter 向文本的第一個(gè)字母添加樣式
:first-line 向文本的首行添加樣式
:before 在元素之前添加內(nèi)容
:after 在元素之后添加內(nèi)容
之前和前端交流頁(yè)面的實(shí)現(xiàn)方案時(shí),經(jīng)常被告知:這個(gè)效果實(shí)現(xiàn)不了;那個(gè)東西兼容性不好;這個(gè)做不了...明明這些效果別人家已經(jīng)實(shí)現(xiàn)出來(lái)了,哎,奈何不懂前端相關(guān),沒(méi)轍!
最近花了點(diǎn)時(shí)間看了些前端相關(guān)的博客、論壇,整理了一些html/css的基礎(chǔ)知識(shí),算是學(xué)個(gè)入門(mén)。同時(shí)還學(xué)了瀏覽器調(diào)試工具的基本用法,做產(chǎn)品測(cè)試的時(shí)候還是蠻有用的,谷歌瀏覽器自帶的調(diào)試工具很好用,火狐的話要裝firebug,有興趣的可以去玩玩,還是很有意思的!
# 基本
html(Hypertext Markup Language)—— 結(jié)構(gòu) 超文本標(biāo)記語(yǔ)言
css(Cascading Style Sheets)—— 樣式 層疊樣式表
js(javascript)—— 行為
html超文本標(biāo)記語(yǔ)言<html> </html> 標(biāo)簽對(duì)
<!DOCTYPE HTML> 聲明文檔類型
<meta charset="utf-8"/> 代碼編碼格式
單標(biāo)簽:直接在后面斜杠結(jié)束的標(biāo)簽叫做單標(biāo)簽。如換行符<br />
行間樣式表<div style="……"></div>
內(nèi)部樣式表<style>…………</style>
外部樣式表<link href="style.css" rel="stylesheet"/>
# 屬性:屬性值;
width 寬度height 高度
background 背景
background-attachment: fixed; 背景是否滾動(dòng)
background-color: gray; 背景顏色
background-image: url(bg.jpg); 背景圖
background-repeat: no-repeat; 背景圖是否重復(fù)
background-position: center 0px; 背景圖位置
# 傳說(shuō)中的盒模型
盒子大小 = border + padding + width/height
盒子寬度 = 左border+左padding+width+右padding +右border
盒子高度 = 上border+上padding+height+下padding+下border
# 基礎(chǔ)屬性
width 寬度 height 高度
background 背景 border 邊框
padding 內(nèi)邊距 margin 外邊距
font-size 文字大小 font-family 字體
color 文字顏色 line-height 行高
text-align 文本對(duì)齊方式 text-indent 首行縮進(jìn)
font-weight 文字著重 font-style 文字樣式
text-decoration 文本修飾 letter-spacing 字母間距
word-spacing 單詞間距
# 基礎(chǔ)標(biāo)簽
div 塊
img 圖片(單標(biāo)簽)
a 鏈接、下載、錨點(diǎn)
h1-h6 標(biāo)題
p 段落
strong 強(qiáng)調(diào)(粗體)
em 強(qiáng)調(diào)(斜體)
span 區(qū)分樣式
ul 無(wú)序列表
ol 有序列表
li 列表項(xiàng)
dl 定義列表
dt 定義列表標(biāo)題
dd 定義列表項(xiàng)
# CSS基礎(chǔ)選擇符
id選擇符(#)
群組選擇符(,)
class選擇符(.)
類型選擇符(div……)
包含選擇符(div p)
通配符(*)
基礎(chǔ)選擇符的優(yōu)先級(jí)
類型 < class < id < style(行間) < js
偽類:偽類用于向被選中元素添加特殊的效果。(元素在特定情況下才具備的。)
a : link 未訪問(wèn)(默認(rèn))
a : hover 鼠標(biāo)懸停(鼠標(biāo)劃過(guò))
a : active 鏈接激活(鼠標(biāo)按下)
a : visited 訪問(wèn)過(guò)后(點(diǎn)擊過(guò)后)
a標(biāo)簽四個(gè)偽類的順序:
link visited hover active
(love hate 記憶方法!)
a偽類的應(yīng)用:
a、四個(gè)偽類全用(搜索引擎、新聞門(mén)戶、小說(shuō)網(wǎng)站)
b、一般網(wǎng)站只用( a{} a:hover{} )
#(樣式重置)css reset 原則
但凡是瀏覽默認(rèn)的樣式,都不要使用。頁(yè)面在不同瀏覽器下總是不能做到很好的兼容,顯示的效果不同,估計(jì)就是一開(kāi)始沒(méi)有做樣式重置!
<style>
body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;font-size:12px;}
ol,ul{margin:0;padding:0;list-style:none;}
a{text-decoration:none;}
img{border:none;}
</style>
# float浮動(dòng):
1、塊在一排顯示
2、內(nèi)聯(lián)支持寬高
3、默認(rèn)內(nèi)容撐開(kāi)寬度
4、脫離文檔流
5、提升層級(jí)半層
float:left | right | none | inherit;
文檔流是文檔中可顯示對(duì)象在排列時(shí)所占用的位置。
浮動(dòng)的定義:使元素脫離文檔流,按照指定方向發(fā)生移動(dòng),遇到父級(jí)邊界或者相鄰的浮動(dòng)元素停了下來(lái)。
clear:left | right | both | none | inherit;元素的某個(gè)方向上不能有浮動(dòng)元素clear:both; 在左右兩側(cè)均不允許浮動(dòng)元素。
# 清浮動(dòng)方法
1.加高問(wèn)題:擴(kuò)展性不好
2.父級(jí)浮動(dòng)問(wèn)題:頁(yè)面中所有元素都加浮動(dòng),margin左右自動(dòng)失效(floats bad ?。?/p>
3.inline-block 清浮動(dòng)方法:?jiǎn)栴}:margin左右自動(dòng)失效;
4.空標(biāo)簽清浮動(dòng)問(wèn)題:IE6 最小高度 19px;(解決后IE6下還有2px偏差)
5.br清浮動(dòng)問(wèn)題:不符合工作中:結(jié)構(gòu)、樣式、行為,三者分離的要求。
6.after偽類 清浮動(dòng)方法(現(xiàn)在主流方法)
.clear:after{content:'';display:block;clear:both;}
.clear{zoom:1;}
after偽類: 元素內(nèi)部末尾添加內(nèi)容;
:after{content"添加的內(nèi)容";} IE6,7下不兼容
zoom 縮放
a、觸發(fā) IE下 haslayout,使元素根據(jù)自身內(nèi)容計(jì)算寬高。
b、FF 不支持;
7.overflow:hidden 清浮動(dòng)方法; 問(wèn)題:需要配合 寬度 或者 zoom 兼容IE6 IE7;
overflow: scroll | auto | hidden;
overflow:hidden;溢出隱藏(裁刀?。?/p>
# 浮動(dòng)兼容性問(wèn)題
IE6雙邊距BUG(IE6下塊屬性標(biāo)簽浮動(dòng),并且有橫向margin,橫向margin加倍。
a、IE6
b、浮動(dòng)
c、橫向margin
d、塊屬性標(biāo)簽(加display:inline;)
IE6下 li部分兼容性問(wèn)題:
a、左右兩列布局,右邊右浮動(dòng)IE6 IE7下折行;(左邊元素浮動(dòng))b、IE6 IE7 li 下元素都浮動(dòng) 在IE6 IE7下 li 下方會(huì)產(chǎn)生4px間隙問(wèn)題;(加vertical-align:top;)
# 定位
position:relative; 相對(duì)定位
a、不影響元素本身的特性;
b、不使元素脫離文檔流;
c、如果沒(méi)有定位偏移量,對(duì)元素本身沒(méi)有任何影響;
定位元素位置控制 top/right/bottom/left 定位元素偏移量。
position:absolute; 絕對(duì)定位
a、使元素完全脫離文檔流;
b、使內(nèi)嵌支持寬高;
c、塊屬性標(biāo)簽內(nèi)容撐開(kāi)寬度;
d、如果有定位父級(jí)相對(duì)于定位父級(jí)發(fā)生偏移,沒(méi)有定位父級(jí)相對(duì)于整個(gè)文檔發(fā)生偏移;
e、相對(duì)定位一般都是配合絕對(duì)定位元素使用;
z-index:[number]; 定位層級(jí) a、定位元素默認(rèn)后者層級(jí)高于前者;
position:fixed; 固定定位
與絕對(duì)定位的特性基本一致,的差別是始終相對(duì)整個(gè)文檔進(jìn)行定位;
問(wèn)題:IE6不支持固定定位;
定位其他值:
position:static ; 默認(rèn)值
position:inherit ; 從父元素繼承定位屬性的值
position:relative | absolute | fixed | static | inherit;
position:relative;在 IE6 下父級(jí)的 overflow:hidden; 包不住子級(jí)的relative;
position:absolute;在 IE6 下定位元素的父級(jí)寬高都為奇數(shù)那么在 IE6 下定位元素的 right 和 bottom 都有1像素的偏差。
position:absolute; 絕對(duì)定位元素子級(jí)的浮動(dòng)可以不用寫(xiě)清浮動(dòng)方法;
position:fixed; 固定定位元素子級(jí)的浮動(dòng)可以不用寫(xiě)清浮動(dòng)方法;(IE6不兼容)
# 表格標(biāo)簽
table 表格
thead 表格頭
tbody 表格主體
tfoot 表格尾
tr 表格行
th 元素定義表頭
td 元素定義表格單元
colspan 屬性規(guī)定單元格可橫跨的列數(shù)。<td colspan="2"></td>rowspan 屬性規(guī)定單元格可橫跨的行數(shù)。<td rowspan="2"></td>
# form 表單
<input type="…… " name="" value="" />
text 文本框
password 密碼
radio 單選
checkbox 復(fù)選
submit 提交
reset 重置
button 按鈕
image 圖片
file 上傳
hidden 隱藏
# 滑動(dòng)門(mén)
滑動(dòng)門(mén)并不是一項(xiàng)全新的技術(shù),它是利用背景圖像的可層疊性,并允許他們?cè)诒舜酥线M(jìn)行滑動(dòng),以創(chuàng)造一些特殊的效果。
CSS Sprites在國(guó)內(nèi)很多人叫CSS精靈,是一種網(wǎng)頁(yè)圖片應(yīng)用處理方式。它允許你將一個(gè)頁(yè)面涉及到的所有零星圖片都包含到一張大圖中去。
CSS Sprites并不是一門(mén)新技術(shù),目前它已經(jīng)在網(wǎng)頁(yè)開(kāi)發(fā)中發(fā)展得十分成熟。大部分公司要求前端工程師必須使用CSS 精靈,處理圖片;
CSS精靈 優(yōu)點(diǎn):利用CSS 精靈能很好地減少了網(wǎng)頁(yè)的http請(qǐng)求次數(shù),從而大大的提高了頁(yè)面的性能,這也是CSS 精靈最大的優(yōu)點(diǎn);減少圖片大小
CSS精靈 缺點(diǎn):
降低開(kāi)發(fā)效率;
維護(hù)難度加大;
# # 立志要搞懂技術(shù)的產(chǎn)品小白,正在學(xué)習(xí)JQuery的路上……
出于對(duì)PMCAFF用戶的尊重,任何在PMCAFF產(chǎn)品經(jīng)理社區(qū)發(fā)布的內(nèi)容,在未經(jīng)允許的情況下,不得在任何平臺(tái)被直接或間接發(fā)布使用或被用于其他任何商業(yè)目的。如有違反上述聲明者本網(wǎng)站將追究其相關(guān)法律責(zé)任。
微信公眾號(hào):pmcaffcom
投稿郵箱:tougao@pmcaff.com
Greated by PMCAFF產(chǎn)品經(jīng)理社區(qū) - www.pmcaff.com
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。