响应式
声明状态
Mithril
import m from "mithril";
export default function Name() {
let name = "张三";
return {
view: () => m("h1", `你好 ${name}`),
};
}
Qwik
import { component$, useSignal } from "@builder.io/qwik";
export const Name = component$(() => {
const name = useSignal("张三");
return <h1>你好 {name.value}</h1>;
});
更新状态
Mithril
import m from "mithril";
export default function Name() {
let name = "张三";
name = "李四";
return {
view: () => m("h1", `你好 ${name}`),
};
}
Qwik
import { component$, useTask$, useSignal } from "@builder.io/qwik";
export const Name = component$(() => {
const name = useSignal("张三");
useTask$(() => {
name.value = "李四";
});
return <h1>你好 {name.value}</h1>;
});
计算属性
Mithril
import m from "mithril";
export default function DoubleCount() {
let count = 10;
let doubleCount = count * 2;
return {
view: () => m("div", doubleCount),
};
}
Qwik
import { component$, useSignal, useComputed$ } from "@builder.io/qwik";
export const DoubleCount = component$(() => {
const count = useSignal(10);
const doubleCount = useComputed$(() => count.value * 2);
return <div>{doubleCount.value}</div>;
});
模板语法
最简模板
Mithril
import m from "mithril";
export default function HelloWorld() {
return {
view: () => m("h1", "你好,世界"),
};
}
Qwik
export const HelloWorld = () => {
return <div>你好,世界</div>;
};
样式
Mithril
import "./style.css";
import m from "mithril";
export default function CssStyle() {
return {
view: () =>
m(
"div",
m("h1.title", "我是红色的"),
m("button", { style: { fontSize: "10rem" } }, "我是一个按钮"),
),
};
}
Qwik
import { component$, useStyles$ } from "@builder.io/qwik";
export const App = component$(() => {
useStyles$(`
.title {
color: red;
}
`);
return (
<>
<h1 class="title">我是红色的</h1>
<button style={{ "font-size": "10rem" }}>我是一个按钮</button>
</>
);
});
DOM循环
Mithril
import m from "mithril";
export default function Colors() {
const colors = ["红", "绿", "蓝"];
return {
view: () =>
m(
"ul",
colors.map((color, idx) => m("li", { key: idx }, color)),
),
};
}
Qwik
import { component$ } from "@builder.io/qwik";
export const Colors = component$(() => {
const colors = ["红", "绿", "蓝"];
return (
<ul>
{colors.map((color) => (
<li key={color}>{color}</li>
))}
</ul>
);
});
点击事件
Mithril
import m from "mithril";
export default function Counter() {
let count = 0;
const incrementCount = () => (count = count + 1);
return {
view: () =>
m(
"div",
m("p", `计数器: ${count}`),
m("button", { onclick: incrementCount }, "+1"),
),
};
}
Qwik
import { component$, useSignal, $ } from "@builder.io/qwik";
export const Counter = component$(() => {
const count = useSignal(0);
const incrementCount = $(() => {
count.value++;
});
return (
<>
<p>计数器: {count.value}</p>
<button onClick$={incrementCount}>Increment</button>
</>
);
});
DOM引用
Mithril
import m from "mithril";
export default function InputFocused() {
let value = "";
return {
view: () =>
m("input", {
oncreate: ({ dom }) => dom.focus(),
type: "text",
value,
oninput: (e) => (value = e.target.value),
}),
};
}
Qwik
import { component$, useVisibleTask$, useSignal } from "@builder.io/qwik";
export const InputFocused = component$(() => {
const inputElement = useSignal<HTMLInputElement>();
useVisibleTask$(({ track }) => {
const el = track(inputElement);
el?.focus();
});
return <input type="text" ref={inputElement} />;
});
条件渲染
Mithril
import m from "mithril";
const TRAFFIC_LIGHTS = ["红灯", "黄灯", "绿灯"];
export default function TrafficLight() {
let lightIndex = 0;
let currentLight = () => TRAFFIC_LIGHTS[lightIndex];
const nextLight = () => (lightIndex + 1) % TRAFFIC_LIGHTS.length;
const instructions = () => {
switch (currentLight()) {
case "红灯":
return "停下";
case "黄灯":
return "慢行";
case "绿灯":
return "赶紧走";
}
};
return {
view: () =>
m(
"div",
m("button", { onclick: nextLight }, "下一个灯"),
m("p", `现在亮着的是: ${currentLight()}`),
m("p", "你应该 ", m("span", instructions())),
),
};
}
Qwik
import { $, component$, useComputed$, useSignal } from "@builder.io/qwik";
export const TRAFFIC_LIGHTS = ["红灯", "黄灯", "绿灯"];
export const TrafficLight = component$(() => {
const lightIndex = useSignal(0);
const light = useComputed$(() => TRAFFIC_LIGHTS[lightIndex.value]);
const nextLight = $(() => {
lightIndex.value = (lightIndex.value + 1) % TRAFFIC_LIGHTS.length;
});
return (
<>
<button onClick$={nextLight}>下一个灯</button>
<p>现在亮着的是: {light.value}</p>
<p>
你应该 {light.value === "红灯" && <span>停下</span>}
{light.value === "黄灯" && <span>慢行</span>}
{light.value === "绿灯" && <span>赶紧走</span>}
</p>
</>
);
});
生命周期
组件加载时
Mithril
import m from "mithril";
export default function PageTitle() {
return {
view: () => m("p", `页面标题: ${document.title}`),
};
}
Qwik
import { component$, useVisibleTask$, useStore } from "@builder.io/qwik";
export const App = component$(() => {
const store = useStore({
pageTitle: "",
});
useVisibleTask$(() => {
store.pageTitle = document.title;
});
return <p>页面标题: {store.pageTitle}</p>;
});
组件卸载时
Mithril
import m from "mithril";
export default function Time() {
let time = new Date().toLocaleTimeString();
const timer = setInterval(() => {
time = new Date().toLocaleTimeString();
m.redraw();
}, 1000);
return {
view: () => m("p", `当前时间: ${time}`),
onremove: () => clearInterval(timer),
};
}
Qwik
import { component$, useVisibleTask$, useStore } from "@builder.io/qwik";
export const App = component$(() => {
const store = useStore({
time: new Date().toLocaleTimeString(),
});
useVisibleTask$(({ cleanup }) => {
const timer = setInterval(() => {
store.time = new Date().toLocaleTimeString();
}, 1000);
cleanup(() => clearInterval(timer));
});
return <p>当前时间: {store.time}</p>;
});
组件组合
组件Props传参
Mithril
import m from "mithril";
import UserProfile from "./UserProfile.js";
export default function App() {
return {
view: () =>
m(UserProfile, {
name: "张三",
age: 20,
favouriteColors: ["green", "blue", "red"],
isAvailable: true,
}),
};
}
Qwik
import { component$ } from "@builder.io/qwik";
import UserProfile from "./UserProfile";
const App = component$(() => {
return (
<UserProfile
name="张三"
age={20}
favouriteColors={["green", "blue", "red"]}
isAvailable
/>
);
});
export default App;
向父组件emit事件
Mithril
import m from "mithril";
export const AnswerButton = ({ attrs: { onYes, onNo } }) => ({
view: () =>
m(
"div",
m("button", { onclick: onYes }, "是的!"),
m("button", { onclick: onNo }, "不是!"),
),
});
Qwik
import { $, component$, useStore } from "@builder.io/qwik";
import AnswerButton from "./AnswerButton";
const App = component$(() => {
const store = useStore({
isHappy: true,
});
const onAnswerNo = $(() => {
store.isHappy = false;
});
const onAnswerYes = $(() => {
store.isHappy = true;
});
return (
<>
<p>你快乐吗?</p>
<AnswerButton onYes$={onAnswerYes} onNo$={onAnswerNo} />
<p style={{ fontSize: 50 }}>{store.isHappy ? "😀" : "😥"}</p>
</>
);
});
export default App;
slot插槽
Mithril
import m from "mithril";
import { FunnyButton } from "./FunnyButton.jsx";
export default function App() {
return {
view: () => m(FunnyButton, "点我!"),
};
}
Qwik
import FunnyButton from "./FunnyButton";
export default function App() {
return <FunnyButton>点我!</FunnyButton>;
}
slot插槽内容
Mithril
import m from "mithril";
import FunnyButton from "./FunnyButton.jsx";
export default function App() {
return {
view: () => m("", m(FunnyButton), m(FunnyButton, "这是传到slot的内容!")),
};
}
Qwik
import FunnyButton from "./FunnyButton";
export default function App() {
return (
<>
<FunnyButton />
<FunnyButton>这是传到slot的内容!</FunnyButton>
</>
);
}
上下文
Mithril
import m from "mithril";
import UserProfile from "./UserProfile";
export default function App() {
const user = {
id: 1,
username: "abcdefg",
email: "[email protected]",
};
const updateUsername = (username) => (user.username = username);
return {
view: () =>
m(
"",
m("h1", `欢迎回来, ${user.username}`),
m(UserProfile, { user, updateUsername }),
),
};
}
Qwik
import {
component$,
useStore,
useContextProvider,
createContext,
$,
} from "@builder.io/qwik";
import UserProfile from "./UserProfile";
export const UserContext = createContext("user-context");
const App = component$(() => {
const user = useStore({
id: 1,
username: "abcdefg",
email: "[email protected]",
});
const updateUsername = $((newUsername) => {
user.username = newUsername;
});
useContextProvider(UserContext, { user, updateUsername });
return (
<>
<h1>欢迎回来, {user.username}</h1>
<UserProfile />
</>
);
});
export default App;
表单输入
输入框
Mithril
import m from "mithril";
export default function InputHello() {
let text = "你好世界";
const handleChange = ({ target: { value } }) => (text = value);
return {
view: () =>
m("", m("p", text), m("input", { value: text, onchange: handleChange })),
};
}
Qwik
import { component$, useSignal } from "@builder.io/qwik";
const InputHello = component$(() => {
const text = useSignal("");
return (
<>
<p>{text.value}</p>
<input bind:value={text} />
</>
);
});
export default InputHello;
复选框
Mithril
import m from "mithril";
export default function IsAvailable() {
let isAvailable = false;
const onUpdate = () => (isAvailable = !isAvailable);
return {
view: () =>
m(
"",
m("input", {
id: "is-available",
type: "checkbox",
checked: isAvailable,
onchange: onUpdate,
}),
m("label", { for: "is-available" }, "这是一个checkbox"),
),
};
}
Qwik
import { component$, useSignal } from "@builder.io/qwik";
const IsAvailable = component$(() => {
const isAvailable = useSignal(false);
return (
<>
<input id="is-available" type="checkbox" bind:checked={isAvailable} />
<label for="is-available">这是一个checkbox</label>
</>
);
});
export default IsAvailable;
单选框
Mithril
import m from "mithril";
export default function PickPill() {
let picked = "red";
let pills = ["red", "green", "blue"];
const handleChange = ({ target: { value } }) => (picked = value);
return {
view: () =>
m(
"",
m("", `你选择了: ${picked}`),
pills.map((pill) =>
m(
".",
m("input", {
id: pill,
checked: picked == pill,
type: "radio",
value: pill,
onchange: handleChange,
}),
m("label", { for: pill }, pill),
),
),
),
};
}
Qwik
import { component$, useSignal } from "@builder.io/qwik";
const PickPill = component$(() => {
const pickedColor = useSignal("red");
return (
<>
<div>你选择了: {pickedColor.value}</div>
<input
id="blue-pill"
type="radio"
bind:value={pickedColor}
checked={pickedColor.value === "blue"}
value="blue"
/>
<label for="blue-pill">蓝色</label>
<input
id="red-pill"
type="radio"
checked={pickedColor.value === "red"}
bind:value={pickedColor}
value="red"
/>
<label for="red-pill">红色</label>
</>
);
});
export default PickPill;
选择器
Mithril
import m from "mithril";
const colors = [
{ id: 1, text: "红" },
{ id: 2, text: "蓝" },
{ id: 3, text: "绿" },
{ id: 4, text: "灰", isDisabled: true },
];
export default function ColorSelect() {
let selectedColorId = 2;
const handleSelect = ({ target: { value } }) => (selectedColorId = value);
return {
view: () =>
m(
"select",
{ value: selectedColorId, onchange: handleSelect },
colors.map(({ id, text, isDisabled }) =>
m("option", { key: id, id, disabled: isDisabled, value: id }, text),
),
),
};
}
Qwik
import { component$, useSignal } from "@builder.io/qwik";
export const colors = [
{ id: 1, text: "红" },
{ id: 2, text: "蓝" },
{ id: 3, text: "绿" },
{ id: 4, text: "灰", isDisabled: true },
];
const ColorSelect = component$(() => {
const selectedColorId = useSignal("2");
return (
<select bind:value={selectedColorId}>
{colors.map((color) => (
<option
key={color.id}
value={color.id}
disabled={color.isDisabled}
selected={`${color.id}` === selectedColorId.value}
>
{color.text}
</option>
))}
</select>
);
});
export default ColorSelect;
Web应用特性
渲染应用
Mithril
<!doctype html>
<html>
<body>
<div id="app"></div>
<script type="module" src="./main.jsx"></script>
</body>
</html>
Qwik
<!doctype html>
<html>
<body>
<div id="app"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
fetch获取数据
Mithril
import m from "mithril";
export default function App() {
let isLoading = false;
let error = null;
let users = [];
async function fetchUsers() {
isLoading = true;
try {
const { results } = await m.request(
"https://randomuser.me/api/?results=3",
);
users = results;
} catch (err) {
error = err;
}
isLoading = false;
}
return {
oninit: fetchUsers,
view() {
if (isLoading) return m("p", "正在获取用户信息……");
if (error) return m("p", "获取用户信息出错");
return users.map((user) =>
m(
"li",
{ key: user.login.uuid },
m("img", { src: user.picture.thumbnail, alt: "user" }),
m("p", `${user.name.first} ${user.name.last}`),
),
);
},
};
}
Qwik
import { component$, useResource$, Resource } from "@builder.io/qwik";
type UsersResponse = {
results: {
picture: {
thumbnail: string;
};
name: {
first: string;
last: string;
};
}[];
};
export async function fetchUsers() {
return (await fetch("https://randomuser.me/api/?results=3")).json();
}
export const App = component$(() => {
const data = useResource$<UsersResponse>(fetchUsers);
return (
<Resource
value={data}
onPending={() => <p>正在获取用户信息……</p>}
onRejected={() => <p>获取用户信息出错</p>}
onResolved={({ results: users }) => (
<ul>
{users.map((user) => (
<li>
<img src={user.picture.thumbnail} alt="user" />
<p>
{user.name.first} {user.name.last}
</p>
</li>
))}
</ul>
)}
/>
);
});