当前位置: 首页 > news >正文

TypeScript 入门

课程地址

ts 开发环境搭建

npm i -g typescript

查看安装位置:

$ npm root -g
C:\Users\Daniel\AppData\Roaming\npm\node_modules

创建 hello.ts

console.log("hello, ts");

编译 ts 文件,得到 js 文件:

$ tsc foo.ts

类型声明

后置的类型声明

let a: number;a = 10;
a = 33;
// a = "hello";let b: string = "hello";
let c = true;   // Automatic type inferencefunction sum(a: number, b: number): number {return a + b;
}console.log(sum(1, 2));

类型

在这里插入图片描述

// 字面量类型
let a: 10;  // const a = 10;// 联合类型
let b: "male" | "female";
let c: boolean | string;
c = true;
c = "hello"// any 与 unknown
let d: any;
d = "hello";let e: unknown;
e = "hello";let s: string = d;
// s = e;   Type 'unknown' is not assignable to type 'string'
s = e as string;
s = <string>e;function f(): void {return;
}function f2(): never {  // 永远不会返回结果,终止进程throw new Error("error");
}

unknown 本质上是一个类型安全的 any

// 对象类型
let b: {name: string,age?: number,   // ? -> optional
};b = {name: "sunwukong"
};let c: {name: string,[propName: string]: any,
};// 函数类型
let d: (a: number, b: number) => number;d = function(a, b) {return a + b;
}// 数组类型
let e: string[];
e = ["a", "b", "c"];let g: Array<number>;
g = [1, 2, 3];// 元组
let h: [string, string];// enum
enum Gender {Male = 0,Female = 1,
}let p: {name: string, gender: Gender}p = {name: "sunwukong",gender: Gender.Male,
}// & 类型
let j: {name: string} & {age: number};// 类型别名
type t1 = 1 | 2 | 3 | 4 | 5;
let m: t1;

tsc 编译选项

自动编译文件

tsc foo.ts -w	# 文件改变时自动编译
tsc		# 根据 tsconfig.json 编译

tsconfig.json

{/*** 表示任意目录* 表示任意文件*/"include": ["./*"],"exclude": ["foo.ts"],// "files": ["./app.ts", "./index.ts"],"compilerOptions": {"target": "ES6",     // 指定 es 版本// "module": "ES6",// "lib": ["DOM"]"outDir": "./dist",// "outFile": "./dist/app.js",  // 将编译结果合并到 app.js"allowJs": false,"checkJs": false,   // 对 js 也做类型检查"removeComments": false,"noEmit": true,     // 不生成编译后的文件,只检查"noEmitOnError": true,   // 当有错误时不生成编译后的文件"strict": true,     // 打开下面的所有严格检查/*"alwaysStrict": true,   // -> use strict"noImplicitAny": true,"noImplicitThis": true,"strictNullChecks": true,*/},
}

使用 webpack 打包 ts 代码

npm init -y		# 初始化项目
npm i -D webpack webpack-cli typescript ts-loader

配置 webpack.config.js

const path = require("path");module.exports = {entry: "./src/index.ts",output: {path: path.resolve(__dirname + "/dist"),filename: "bundle.js",},module: {rules: [{test: /\.ts$/,use: "ts-loader",exclude: /node_modules/}]},mode: 'development',
}

配置 tsconfig.json

{"compilerOptions": {"module": "es6","target": "es6","strict": true,}
}

package.json 中增加 script 配置

  "scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack"},

最后 npm run build 即可

安装 3 个插件:

npm i -D html-webpack-plugin	# 生成 html 测试文件
npm i -D webpack-dev-server		# 编辑后自动重启服务
npm i -D clean-webpack-plugin	# 清除 dist 目录再构建
npm i -D @babel/core @babel/preset-env babel-loader core-js

package.json 中编写 webpack-server 的启动脚本:

  "scripts": {"start": "webpack serve --open"},

webpack.config.ts 中引入插件(使用 template.html 作为生成模板):

const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");module.exports = {entry: "./src/index.ts",output: {path: path.resolve(__dirname + "/dist"),filename: "bundle.js",},module: {rules: [{test: /\.ts$/,use: [{loader: "babel-loader",options: {presets: [["@babel/preset-env",{targets: {"chrome": "88"},"corejs": "3","useBuiltIns": "usage"  // 按需加载}]]}},"ts-loader"],exclude: /node_modules/}]},mode: 'development',plugins: [new HTMLWebpackPlugin({// title: "mytitle"template: "./src/template.html"}),new CleanWebpackPlugin(),],resolve: {  // 设置引用模块extensions: [".ts", ".js"]}
}

OOP

class

class Person {name: string = "sunwukong";static age: number = 18;readonly gender: string = "M";sayHello() {console.log("hello");}
}let p = new Person();console.log(p);
console.log(Person.age);
p.sayHello();

构造函数和 this

class Dog {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}bark() {console.log(`${this.name}: wangwang`);}
}let d = new Dog("wangcai", 4);console.log(d);
d.bark();

继承

class Animal {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}sayHello() {console.log("sayHello()");}
}class Dog extends Animal{run(){console.log(`${this.name} is running`);}sayHello(): void {  // 子类重写父类方法console.log("wangwangwang");}
}class Cat extends Animal{sayHello(): void {  // 子类重写父类方法console.log("miaomiaomiao");}
}const d1 = new Dog("wangcai", 5);
console.log(d1);
d1.sayHello();const c1 = new Cat("mimi", 3);
c1.sayHello();

super

super 表示父类

class Animal {name: string;constructor(name: string) {this.name = name;}sayHello() {console.log("sayHello()");}
}class Dog extends Animal {age: number;constructor(name: string, age: number) {super(name);this.age = age;}sayHello(): void {console.log("wangwangwang");}
}const d1 = new Dog("wangcai", 3);
console.log(d1);    // Dog { name: 'wangcai', age: 3 }
d1.sayHello();      // wangwangwang

抽象类

使用 abstract 修饰的类,不能用于实例化对象,只能被继承

抽象类中可以添加抽象方法,抽象方法必须被子类重写

abstract class Animal {name: string;constructor(name: string) {this.name = name;}abstract sayHello(): void;  // 必须被子类重写
}class Dog extends Animal {sayHello(): void {console.log("wangwangwang");}
}const d1 = new Dog("wangcai");
console.log(d1);    // Dog { name: 'wangcai', age: 3 }
d1.sayHello();      // wangwangwang

接口

用来定义一个类的结构(一个类应该包含哪些属性和方法,做类型限制)

接口中的所有属性都不能带有实际的值,接口只定义对象的结构(全是抽象方法),而不考虑实际值

interface Person{name: string;age: number;
}// 接口可以分离定义
interface Person {gender: string;sayHello(): void;
}class Male implements Person {name: string;age: number;gender: string;constructor(name: string, age: number, gender: string) {this.name = name;this.age = age;this.gender = gender;}sayHello(): void {console.log("hello");}
}

属性访问控制

class Person {private _name: string;private _age: number;constructor(name: string, age: number) {this._name = name;this._age = age;}get name() {        // per.namereturn this._name;}set name(name: string) {this._name = name;}get age() {return this._age;}set age(age: number) {if (age >= 0) {this._age = age;}}
}const p1 = new Person("sunwukong", 18);
console.log(p1);p1.name = "zhubajie";
console.log(p1);
console.log(p1.age);class C {// 直接将属性和访问控制定义在构造函数中constructor(public name: string, public age: number) {}
}

泛型

function fn<T> (a: T): T {return a;
}fn(10);
fn<string>("hello");function bar<T, K>(a: T, b: K): T {console.log(b);return a;
}bar<number, string>(10, "hello");interface Inter {length: number;
}
// 使用接口约束泛型的类型参数
function foo<T extends Inter>(a: T): number {return a.length;
}foo("123");     // string 有 length
foo({length: 10});// 类的泛型
class C<T> {name: T;constructor(name: T) {this.name = name;}
}const c = new C<string>("sunwukong");

贪吃蛇练习

// package.json
{"name": "snake","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack","start": "webpack serve --open"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/core": "^7.23.9","@babel/preset-env": "^7.23.9","babel-loader": "^9.1.3","clean-webpack-plugin": "^4.0.0","core-js": "^3.35.1","css-loader": "^6.10.0","html-webpack-plugin": "^5.6.0","less": "^4.2.0","less-loader": "^12.2.0","style-loader": "^3.3.4","ts-loader": "^9.5.1","typescript": "^5.3.3","webpack": "^5.89.0","webpack-cli": "^5.1.4","webpack-dev-server": "^4.15.1"}
}
// tsconfig.json
{"compilerOptions": {"module": "es6","target": "es6","strict": true,"noEmitOnError": true}
}
// webpack.config.ts
const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");module.exports = {entry: "./src/index.ts",output: {path: path.resolve(__dirname + "/dist"),filename: "bundle.js",},module: {rules: [{test: /\.ts$/,use: [{loader: "babel-loader",options: {presets: [["@babel/preset-env",{targets: {"chrome": "88"},"corejs": "3","useBuiltIns": "usage"  // 按需加载}]]}},"ts-loader"],exclude: /node_modules/},{test: /\.less$/,use: ["style-loader","css-loader","less-loader"]}]},mode: 'development',plugins: [new HTMLWebpackPlugin({// title: "mytitle"template: "./src/template.html"}),new CleanWebpackPlugin(),],resolve: {  // 设置引用模块extensions: [".ts", ".js"]}
}
// index.ts
import "./style/index.less"
import Food from "./modules/food"
import ScorePanel from "./modules/score_panel"
import GameControl from "./modules/game_control";const f = new Food();
f.change();const gc = new GameControl();
// snake.ts
class Snake {head: HTMLElement;bodies: HTMLCollection;element: HTMLElement;constructor() {this.head = document.querySelector("#snake > div")!;this.bodies = document.getElementById("snake")!.getElementsByTagName("div");this.element = document.getElementById("snake")!;}get X() {return this.head.offsetLeft;}get Y() {return this.head.offsetTop;}set X(x: number) {if (this.X === x) return;if (x < 0 || x > 290) {throw new Error("snake_dead");}if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === x) {if (x > this.X) {x = this.X - 10;} else {x = this.X + 10;}}this.moveBody();this.head.style.left = x + "px";this.checkHeadBody();}set Y(y: number) {if (this.Y === y) return;if (y < 0 || y > 290) {throw new Error("snake_dead");}if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === y) {if (y > this.Y) {y = this.Y - 10;} else {y = this.Y + 10;}}this.moveBody();this.head.style.top = y + "px";this.checkHeadBody();}addBody() {this.element.insertAdjacentHTML("beforeend", "<div></div>");}moveBody() {for (let i = this.bodies.length - 1; i > 0; i--) {let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;(this.bodies[i] as HTMLElement).style.left = X + "px";(this.bodies[i] as HTMLElement).style.top = Y + "px";}}checkHeadBody() {for (let i = 1; i < this.bodies.length; i++) {let bd = this.bodies[i] as HTMLElement;if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {throw new Error("snake_dead");}}}
}
export default Snake;
// score_panel.ts
class ScorePanel {score = 0;level = 1;scoreEle: HTMLElement;levelEle: HTMLElement;maxLevel: number;upScore: number;constructor(maxLevel: number = 10, upScore: number = 10) {this.scoreEle = document.getElementById("score")!;this.levelEle = document.getElementById("level")!;this.maxLevel = maxLevel;this.upScore = upScore;}addScore() {this.scoreEle.innerHTML = ++this.score + "";if (this.score % this.upScore === 0) {this.levelUp();}}levelUp() {if (this.level < this.maxLevel) {this.levelEle.innerHTML = ++this.level + "";}}
}
export default ScorePanel;
// game_control.ts
import Snake from "./snake";
import Food from "./food";
import ScorePanel from "./score_panel";class GameControl {snake: Snake;food: Food;scorePanel: ScorePanel;direction: string = "";isLive = true;constructor() {this.snake = new Snake();this.food = new Food();this.scorePanel = new ScorePanel();this.init();}keydownHandler = (event: KeyboardEvent) => {this.direction = event.key;}init() {document.addEventListener("keydown", this.keydownHandler);this.run();}run() {let X = this.snake.X;let Y = this.snake.Y;switch(this.direction) {case "ArrowUp":Y -= 10;break;case "ArrowDown":Y += 10;break;case "ArrowLeft":X -= 10;break;case "ArrowRight":X += 10;break;}this.checkEat(X, Y);try {this.snake.X = X;this.snake.Y = Y;} catch (e) {alert((e as Error).message);this.isLive = false;}this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);}checkEat(x: number, y: number) {if (x === this.food.X && y === this.food.Y) {console.log("ate food");this.food.change();this.scorePanel.addScore();this.snake.addBody();}}}export default GameControl;
// food.ts
class Food {element: HTMLElement;constructor() {// `!` 表示判定该语句的结果不可能为空this.element = document.getElementById("food")!;}get X() {return this.element.offsetLeft;}get Y() {return this.element.offsetTop;}change() {let left = Math.round(Math.random() * 29)* 10;let top = Math.round(Math.random() * 29)* 10;this.element.style.left = top + "px";this.element.style.top = left + "px";}
}
export default Food;
// index.less
* {margin: 0;padding: 0;box-sizing: border-box;
}body {font: bold 20px Courier;
}#main {width: 360px;height: 420px;background-color: #b7d4a8;margin: 100px auto;border: 10px solid black;border-radius: 10px;display: flex;flex-flow: column;align-items: center;justify-content: space-around;#stage {width: 304px;height: 304px;border: 2px solid black;position: relative;#snake {&>div {width: 10px;height: 10px;background-color: black;border: 1px solid #b7d4a8;position: absolute;}}#food {width: 10px;height: 10px;position: absolute;left: 40px;top: 100px;display: flex;flex-flow: row wrap;justify-content: space-between;align-content: space-between;&>div {width: 4px;height: 4px;background-color: black;}}}#score-panel {width: 300px;display: flex;justify-content: space-between;}}

相关文章:

TypeScript 入门

课程地址 ts 开发环境搭建 npm i -g typescript查看安装位置&#xff1a; $ npm root -g C:\Users\Daniel\AppData\Roaming\npm\node_modules创建 hello.ts&#xff1a; console.log("hello, ts");编译 ts 文件&#xff0c;得到 js 文件&#xff1a; $ tsc foo.…...

linux 磁盘相关操作

1.U盘接入虚拟机 &#xff08;1&#xff09;在插入u盘时&#xff0c;虚拟机会检测usb设备&#xff0c;在弹出窗口选择连接到虚拟机即可。 &#xff08;2&#xff09;或 直接在虚拟机--->可移动设备--->找到U盘---->连接 2.检测U盘是否被虚拟机识别 ls /dev/sd* 查…...

PyTorch: torch.max()函数详解

torch.max函数详解&#xff1a;基于PyTorch的深入探索 &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;torch.max()函数简介&#x1f333;&#x1f333;torch.max()的返回值&#x1f333;&#x1f333;torch.max()的应用示例&#x1f333;&am…...

Rust基础拾遗--核心功能

Rust基础拾遗 前言1.所有权与移动1.1 所有权 2.引用3.特型与泛型简介3.1 使用特型3.2 特型对象3.3 泛型函数与类型参数 4.实用工具特型5.闭包 前言 通过Rust程序设计-第二版笔记的形式对Rust相关重点知识进行汇总&#xff0c;读者通读此系列文章就可以轻松的把该语言基础捡起来…...

MySQL:常用指令

MySQL官网 一、在Windows 系统 cmd窗口里执行的命令 启动:net start MySQL停止:net stop MySQL卸载:sc delete MySQL 二、在macOS系统终端里执行的命令 启动&#xff1a;mysql.server start停止&#xff1a;mysql.server stop重启&#xff1a;mysql.server restart 三、执行帮…...

Scrapy:Python中强大的网络爬虫框架

Scrapy&#xff1a;Python中强大的网络爬虫框架 在当今信息爆炸的时代&#xff0c;从互联网上获取数据已经成为许多应用程序的核心需求。Scrapy是一款基于Python的强大网络爬虫框架&#xff0c;它提供了一种灵活且高效的方式来提取、处理和存储互联网上的数据。本文将介绍Scrap…...

linux系统非关系型数据库redis的配置文件

redis配置文件 Redis的配置文件位于Redis安装目录下&#xff0c;文件名为redis.conf&#xff0c;配置项说明如下 Redis默认不是以守护进程的方式运行&#xff0c;可以通过该配置项修改&#xff0c;使用yes启用守护进程 daemonize no当Redis以守护进程方式运行时&#xff0c;Red…...

电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python) 源码设计 #------------------...

Java+SpringBoot实习管理系统探秘

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…...

c入门第十六篇——学生成绩管理系统

师弟&#xff1a;“师兄&#xff0c;我最近构建了一个学生成绩管理系统&#xff0c;有空试用一下么&#xff1f;” 我&#xff1a;“好啊&#xff01;” 一个简单的学生成绩管理系统&#xff0c;基本功能包括&#xff1a;添加学生信息、显示所有学生信息、按学号查找学生信息、…...

大文件上传如何做断点续传?

文章目录 一、是什么分片上传断点续传 二、实现思路三、使用场景小结 参考文献 一、是什么 不管怎样简单的需求&#xff0c;在量级达到一定层次时&#xff0c;都会变得异常复杂 文件上传简单&#xff0c;文件变大就复杂 上传大文件时&#xff0c;以下几个变量会影响我们的用…...

SpringCloud-Eureka原理分析

Eureka是Netflix开源的一款用于实现服务注册与发现的工具。在微服务架构中&#xff0c;服务的动态注册和发现是必不可少的组成部分&#xff0c;而Eureka正是为了解决这一问题而诞生的。 一、为何需要Eureka 在微服务架构中&#xff0c;服务之间的协同合作和高效通信是至关重要…...

LeetCode周赛——384

1.修改矩阵&#xff08;模拟&#xff09; class Solution { public:vector<vector<int>> modifiedMatrix(vector<vector<int>>& matrix) {int n matrix.size();int m matrix[0].size();vector<int> ans(m);for(int i 0; i < m; i)for(…...

C#,巴都万数列(Padonve Number)的算法与源代码

1 巴都万数列&#xff08;Padovan Sequence&#xff09; 巴都万数列&#xff08;Padovan Sequence&#xff09;是一个整数数列。 首数个值为1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28, 37 ... 此数列以建筑师理察巴都万命名&#xff0c;他的论文Dom&#xff08;1994年&a…...

NSSCTF Round#18 RE GenshinWishSimulator WP

恶搞原神抽卡模拟器 看到软件的界面&#xff0c;大致有三种思路&#xff1a; 修改石头数量一直抽&#xff0c;如果概率正常肯定能抽到&#xff08;但是估计设置的概率是0&#xff09;在源码里找flag的数据把抽卡概率改成100%直接抽出来 Unity逆向&#xff0c;根据经验应该dnsp…...

鸿蒙系统对应安卓版本

鸿蒙系统对应安卓版本 使用安卓studio 新建一个app 然后添加代码打印&#xff1a; Log.d(“MainActivity”, "SDK Version: " Build.VERSION.SDK_INT); 或者把 Build.VERSION.SDK_INT 添加到显示的字符串上面 我这里 build.gradle.kts 配置 android {compileSdk…...

算法-16-并查集

并查集简介 并查集&#xff1a;一开始&#xff0c;把a&#xff0c;b&#xff0c;c放入并查集&#xff0c;a自己一个集合&#xff0c;b自己一个&#xff0c;c自己一个 提供的方法 1.boolean isSameSet(a,b)&#xff0c;判断ab是否在同一个集合 2.void union(a,b),把a所…...

【C/C++】2024春晚刘谦春晚魔术步骤模拟+暴力破解

在这个特别的除夕夜&#xff0c;我们不仅享受了与家人的温馨团聚&#xff0c;还被电视机前的春节联欢晚会深深吸引。特别是&#xff0c;魔术师刘谦的精彩表演&#xff0c;为我们带来了一场视觉和心灵的盛宴。在我的博客“【C/C】2024春晚刘谦春晚魔术步骤模拟暴力破解”中&…...

Java运算符和表达式

Java运算符和表达式 和C语言一样&#xff0c;java也有基础的运算符和表达式&#xff0c;用来完成一些基础的数学计算&#xff0c;以及逻辑运算&#xff0c;我们一起来学习一下吧。 算数运算符 首先&#xff0c;这个算数运算符与数学中即C语言的运算符的功能一样&#xff0c;利…...

波奇学Linux:软硬链接

ln指令建立链接 软链接 硬链接 所属者的前的数字表示硬链接数&#xff0c;引用计数&#xff0c;file.txt和soft_link是软链接所以都为2 软链接有独立inode&#xff0c;硬链接没有&#xff0c;所以硬链接不是独立文件&#xff0c;软链接是独立文件&#xff0c;且硬链接的属性会…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...