Normal view
“Modern Love”: What is a Mistress Dispeller?
Medium and Message: Painting with egg yolk
If you’ve left plates coated with egg for a while, you’ll know how difficult its residue can be to remove. No one knows when people first took advantage of this in paints, but earliest surviving examples date from late classical times. By the Renaissance, egg yolk was popular as a binder in artists’ paints, and the technique of egg tempera was used to create many of the masterpieces of the day.
Pure egg tempera technique uses the proteins, fats and other constituents of the yolk of fresh hens’ eggs as its binder; being water-based, water is its diluent. Applied thinly to an absorbent ground such as powdered chalk in a gesso, this quickly sets to form a hard if not brittle paint layer which, unlike glue tempera, can’t readily be removed by water.
Because egg tempera forms such a hard paint layer but is applied thinly, it’s prone to cracking unless the support is rigid and doesn’t change dimensions over time. Early egg tempera paintings were almost exclusively made on wood, but more recently stretched canvas has been used instead. That can lead to cracks and eventual mechanical failure of the paint layer. Egg tempera on wood panel was the favoured combination for easel paintings during the early Renaissance, particularly in Italy.
The finest paintings in egg tempera use only fresh eggs; as eggs age, particularly when they’re not refrigerated, separating the yolk becomes more difficult, and the resulting paint layer doesn’t appear as strong.
Since the nineteenth century, some paint manufacturers such as Sennelier have offered tubed paints with egg as their main binder, but with the addition of some drying oil to form an egg-oil emulsion. These have some of the properties of pure egg tempera, but are more versatile in their handling, and can be used like gouache and even, to a degree, like oil paints. These appear to have been derived from recipes recorded during the Renaissance.

Earliest European examples of egg tempera, such as Margarito d’Arezzo’s The Virgin and Child Enthroned, with Scenes of the Nativity and the Lives of the Saints from the middle of the thirteenth century, often incorporate extensive gilding and today might appear ‘primitive’.

Even the earliest paintings in egg tempera can be remarkably well preserved, such as Duccio’s Healing of the Man born Blind from the early fourteenth century. Although it only forms a thin paint layer, egg yolk is sufficient to preserve high levels of chroma in the pigments.

As the modelling of flesh and clothing became more realistic, egg tempera proved more than sufficient for the task.

One of the finest early works painted entirely in egg tempera is the anonymous Wilton Diptych in London’s National Gallery. Thought to have been made in France at the end of the fourteenth century, its exquisite detail would have been painted in multiple thin layers using fine brushes, much like miniatures painted on vellum.


But it was in Italy that painting in egg tempera reached its apogee, with masters like Masaccio, in his Santa Maria Maggiore Altarpiece from about 1428-29 (above) and Piero della Francesca’s Baptism of Christ (below) of a decade later.

During the fifteenth century, egg tempera was progressively replaced by oils in Italy, as it had been earlier in the Northern Renaissance.

Uccello’s large panel of the Battle of San Romano incorporated some drying oils, including walnut and linseed, although it was still fundamentally painted in egg tempera.

By the end of the fifteenth century, many studios had changed to oils. Among the last large egg tempera paintings are Botticelli’s Primavera (above) and The Birth of Venus (below), from the 1480s. The craft labour involved in producing these large works must have been enormous. Although Primavera was painted on a panel, Venus is on canvas, making it more manageable given its size of nearly 2 x 3 metres (79 x 118 inches).


In the closing years of the fifteenth century, Michelangelo kept to the hallowed tradition of egg tempera on wood in this unfinished painting of the Virgin and Child known now as The Manchester Madonna. This shows how he painstakingly completed each of the figures before moving onto the next, and the characteristic green earth ground.
By this time, the only common use for egg tempera was in the underpainting before applying oils on top. This remains a controversial practice: performed on top of gesso ground it can be successful, but increasingly studios transferred to oils. Egg tempera didn’t completely disappear, though. With so many fine examples of how good its paintings both look and age, there were always some artists who have chosen it over oils.

Some nineteenth century movements that aimed to return to the more wonderful art of the past experimented again with egg tempera. In the late 1870s, John Roddam Spencer Stanhope started to use the medium, and made one of his finest works, Love and the Maiden (1877), using it.

A later exponent who was rigorous in his technique was Adrian Stokes, who used it to great effect in this landscape of Autumn in the Mountains in 1903.
But for my taste, the greatest painter in egg tempera since the Renaissance has to be one of the major artists of the twentieth century: Andrew Wyeth (1917–2009). As his works remain in copyright, I recommend that you browse his official site, where you can see just how effective egg tempera can be in the hands of a great master. It may not be as popular as in the past, but egg tempera still has a great deal to offer.

Medium and Message: Painted walls and ceilings
Earlier in this series, I showed an example of the origin of all modern paintings in the decorated caves of pre-history. As shelters became buildings, our ancestors continued to paint their walls, using a technique now known as secco wall or mural painting, where wet paint is applied to dry stone or plaster.

Secco has been widely used outside Europe, and can still be seen in some very old European wall paintings, such as those in Braunschweig Cathedral, including Christ Pantocrator, thought to date from the early thirteenth century. Long before that was created, though, wall painting advanced to improve adhesion between its paint layer and ground. In secco technique, only thin layers of pigment can be used, resulting in weak colours, little detail, and the need for periodic re-painting as pigment is gradually lost over time.
Artists experimented with different binders and secco techniques. Although dry plaster is more absorbent and a better ground than bare stone, success has been limited, and failure a constant danger. At some time before about 1700 BCE, one of the Mediterranean cultures discovered that it was possible to apply paint onto a layer of wet plaster, and the technique of fresco (strictly, buon fresco) was born.

The Romans loved frescos that made their rooms look as if they were in a spacious outdoors, like this from the House of the Golden Bracelet in Pompeii.
In fresco, the support remains the wall or ceiling of the building, but the ground is absorbent wet plaster applied to that surface. Pigment is diluted in water and applied directly to the ground while the latter is still wet; this allows the paint to be absorbed into the ground, providing good and durable bonding of the pigment. Plaster is made using lime, derived from crushed limestone, and sets by reaction with carbon dioxide in the air to form calcium carbonate (from which both chalk and limestone are composed) and water, which evaporates during drying.
Techniques became even more refined, with the use of additional layers of plaster prepared in specific ways, to which red pigment sinopia might be added, allowing the artist to draw construction and other lines to assist in final painting. Because these frescos are on a grand scale, transferring the design of a painting from final sketch to the wall or ceiling is also a challenge.
The central problem for the painter is that, to be successful, fresco has to be painted onto the plaster when it is still wet. That means only a limited area can be plastered and painted each day, known as giornate (singular giornata), a day’s work. For all but the smallest of ground-level fresco paintings, work has to be undertaken at height, from a scaffold, posing the very real risk that the artist would fall, or the scaffolding fail. Many fresco painters have fallen at work, some suffering serious injuries or death as a result.
Despite all these practical difficulties, some of the most important European works of art are frescos painted during the Renaissance in places of worship with their high walls and ceilings.

Masaccio’s magnificent fresco of The Holy Trinity in the Basilica of Santa Maria Novella in Florence, was painted in 1426-28. At the outset he would have made a preliminary plan including his giornate starting at the top and working downwards. Once ready to start the painting, a team of carpenters will have erected wooden scaffolding to give the artist and his assistants access to the whole of that section of the wall, to the full height of over six metres (21 feet). The first stage would then have been completed by assistants, who laid a rough under-layer of plaster known as the arriccio over the whole wall, and left it to dry for several days. This layer often contains abrasive sand particles to provide a key for the final layer of plaster.
Once that had dried completely, Masaccio and his assistants transferred the drawings onto the surface of the arriccio. This may have been performed by scaling up from the squared drawing and painting with sinopia, or full-size drawings may have been pricked to make holes in the paper and a bag of soot banged against the sheet held against the wall, a technique known as pouncing. Masaccio is known to have used both techniques, and may well have used each in different sections of this work.
On each day of painting, assistants would prepare the colours by mixing pigments in water. The day’s supply of plaster, the intonaco, is then prepared by mixing water with lime. That day’s giornata is covered with a thin layer of intonaco, and about an hour later Masaccio started painting into it. He then had about eight hours before it dried and he could apply no more fresh paint. Like many of the best fresco painters, Masaccio extended his painting time by using paint mixed with milk or casein and a little lime, effectively a lime-based casein medium, which could be laid onto dry intonaco.
The geometric requirements of this painting also merited special measures. When the intonaco was first applied, it was marked to indicate key construction lines, such as those in the barrel-vaulted ceiling, and down the pillars at the side. The remains of these incised lines are still visible when the fresco is viewed in raking light. In this case, there is evidence that Masaccio used lengths of string attached to a nail sunk at the vanishing point of the linear projection, below the base of the cross.

Although we think of frescos as being fixed, this one has now been moved twice within the same church, which hasn’t helped its appearance. During conservation work and movement of Masaccio’s painting in the 1950s, the opportunity was taken to study its construction. Leonetto Tintori drew up a plan of all the identified construction lines and edges of giornate; I have sketched in the latter from a reproduction of a drawing made at that time, which has since been destroyed.
It’s estimated the whole painting would have required some 24 giornate, although because of the long history of damage and attempts at its restoration, that number remains flexible. Assuming that Masaccio painted six days a week, that would have required a minimum of four weeks working for at least ten hours each day. Fresco painting doesn’t permit easy alterations either: if any repainting was required and couldn’t be accomplished using dry technique, that day’s giornata would have to be removed, replaced and repainted.

Giornate can sometimes become obvious over time, as shown in Giotto’s fresco in the Scrovegni Chapel in Padua, Italy.

Perhaps the most famous fresco is Michelangelo’s The Last Judgement (1536-41) in the Sistine Chapel of the Vatican. It covers an area of 13.7 by 12 metres (539 by 472 inches), and took over four years to complete.
In the eighteenth and nineteenth centuries, fresco became relatively neglected.

Some new frescos were commissioned for places of worship and other public buildings, and in the early nineteenth century Johann Friedrich Overbeck painted a series telling the story of Torquato Tasso’s epic Jerusalem Delivered, in the Casa Massimo in Rome. There are similar series showing Dante’s Divine Comedy and other long narratives, which are particularly suited to the medium.

In 1845, the Scottish artist William Dyce was invited to paint frescos for the Royal Family, for which he travelled to Italy to learn technique. On his return in 1847, he painted this curious composition in Queen Victoria and Prince Albert’s new and luxurious holiday palace of Osborne House, at East Cowes, on the Isle of Wight. Neptune Resigning to Britannia the Empire of the Sea (1847) is an impressive fresco, and remains in pristine condition at the top of the main staircase in the house.

Frescos have continued in religious painting, with artists such as Sergei Fyodorov painting them in churches and cathedrals, and for the occasional trompe l’oeil. John Dixon Batten’s The Creation of Pandora was painted anachronistically in egg tempera on a fresco ground by 1913. Batten was one of the late adherents of the Pre-Raphaelite movement; this painting was deemed unfashionable in 1949, and was put into storage and quietly forgotten until its rediscovery in 1990.

New frescos are still painted in some public buildings too. This work by Finnish painter Akseli Gallen-Kallela is one of a series he painted in the National Museum of Finland in Helsinki in 1928, but most other wall paintings of the twentieth century, such as those of John Singer Sargent in public buildings in Boston, have been painted in oils on canvas rather than buon fresco.
Frescos aren’t the only way of making very large and monumental paintings for places like churches, though. The walls of Venetian buildings are particularly unsuitable for secco or fresco, because they remain so damp all year round. Hence the painters of Venice were innovators in constructing very large canvases, and you will find few frescos there as a result.

Reading Visual Art: 227 Chicken
The humble domestic chicken is probably the most common and widely distributed farm animal. It originated in about 8,000 BCE in south-east Asia and spread its way steadily across every continent except Antarctica. It probably reached Europe before the Roman Empire, and since then has been commonplace. Perhaps because of its small size and frequent presence, it features in relatively few paintings.
The cruel sport of cockfighting accompanied its spread, and is depicted in Jean-Léon Gérôme’s first successful painting in 1846.

This motif had started from a relief showing two adolescent boys facing off against one another. Gérôme felt he needed to improve his figurative painting, and after Delaroche’s advice decided to develop that image by replacing one of the boys with a girl. In both Greek and English (but not French) the word cock is used for both the male genitals and a male chicken, and the youthful Gérôme must have found this combined visual and verbal pun witty and very Neo-Greek.
There’s a curious ambivalence in its reading too: two cocks are fighting in front of the young couple. Is one of the birds owned by the girl, and if so, is it the dark one on the left, which appears to be getting the better of the bird being held by the boy? Either way, it’s a lightly entertaining reflection on courtship and gender roles, and a promising debut. The Cock Fight earned Gérôme a third-class medal, and he sold the painting for a thousand francs. With the benefit of favourable reviews from critics, the following year brought him lucrative commissions, and a growing reputation.
A dead chicken plays a significant role in one of Rembrandt’s most famous group portraits.

His vast group portrait of The Night Watch (1642) is the most famous of all those of militia in the Dutch Republic. It features the commander and seventeen members of his civic guard company in Amsterdam. Captain Frans Banninck Cocq (in black with a red sash), followed by his lieutenant Willem van Ruytenburch (in yellow with a white sash) are leading out this militia company, their colours borne by the ensign Jan Visscher Cornelissen. The small girl to the left of them is carrying a dead chicken, a curious symbol of arquebusiers, the type of weapon several are carrying.
For a young child, cockerels can appear large and threatening, as used by Gaetano Chierici in a delightful visual joke.

His undated painting of A Scary State of Affairs calls on our experience of the behaviour of cockerels and geese. An infant has been left with a bowl on their lap, and their room is invaded first by cockerels, then by those even larger and more aggressive geese. The child’s eyes are wide open, their mouth at full stretch in a scream, their arms raised, and their legs are trying to fend the birds away.
Being among the most humble and everyday domestic species, chickens seldom make the limelight in religious narratives.

Murillo’s Adoration of the Shepherds from about 1650 is an exception featuring unusual additional details including the old woman carrying a basketful of eggs, and chickens in front of the kneeling shepherd.
In most paintings including chickens, though, they are just extras in the farmyard.

Paulus Potter’s Figures with Horses by a Stable (1647) includes finely painted horses, chickens, a dog, and distant cattle, with a magnificent tree in the centre and a sky containing several birds.

In Chickenfeed from 1867, Hans Thoma tackles this genre scene in a traditional and detailed realist style.

Alberto Pasini’s Market Scene from 1884 has an eclectic mixture of produce, ranging from live chickens to pots and the artist’s signature melons.

Évariste Carpentier’s undated Eating in the Farmyard, an example of the rural deprivation which sparked Naturalist art, shows two kids surrounded by animals and birds in this much-used space.

Here, Carpentier’s old woman is busy Feeding the Chickens.

Friedrich Eckenfelder’s Zollernschloss, Balingen from about 1884-5 shows a small yard just below the back of this castellated farm in Germany, with its lively flock of chickens.

Gustav Klimt had probably painted his first small landscapes between 1881-87, and returned to the genre more seriously in about 1896. This work, variously known as After the Rain, Garden with Chickens in St. Agatha, or similar, is thought to have been painted when Klimt stayed in the Goiserer Valley with the Flöge family in the summer of 1898.
Very occasionally, a chicken may come as something of a surprise.

Hieronymus Bosch’s Ship of Fools, a fragment from a larger Wayfarer triptych painted in 1500-10, is actually a small boat, into which six men and two women are packed tight. Its mast is unrealistically high, bears no sail, and has a large branch lashed to the top of it, in which is Bosch’s signature owl. Its occupants are engaged in drinking, eating what appear to be cherries from a small rectangular tabletop, and singing to the accompaniment of a lute being played by one of the women. A man has climbed a tree on the bank to try to cut down the carcass of a chicken from high up the mast.

William Blake’s mythology: The Ancient of Days
One of the golden rules in narrative painting is to tell stories that the viewer is already familiar with, because of the limitations imposed by still images. By the middle of the nineteenth century, though, artists were breaking that rule in what became a new sub-genre of the ‘problem picture’, with open-ended narrative encouraging the viewer to construct their own stories. William Blake was a precursor to that in some of his paintings, and this weekend I look at two examples that try to tell stories we’re unfamiliar with.

At first sight, Blake’s painted etching The Ancient of Days from about 1821 might represent the Christian God seen as master craftsman, forming the world out of the darkness below heaven. That would be an innovative but hardly revolutionary interpretation of the opening of the book of Genesis.
That wasn’t Blake’s intention, though. This represents Urizen, one of many figures from his own mythology, and documented only in the artist’s writings. There, Urizen symbolises reason, his name most probably a semi-conscious pun on your reason. This painting shows Urizen the architect, creating the world using his compasses. He goes on to have the role of the jealous and vengeful god of the Old Testament, but his desire for dominion brings about his downfall into a state of Satan.
Representations of God as architect aren’t common, but Blake’s would be by no means unique.

This frontispiece to a Bible Moralisée from around 1220-30 shows the Christian God as architect, using his compasses during the creation of the world. The compasses continue in various modern symbols, including those that feature in freemasonry, and in its references to the Supreme Being as the Great Architect of the Universe.

Urizen typically appears with long and streaming white hair and beard, as in Blake’s plates throughout his First Book of Urizen from 1796.

But Urizen isn’t the only figure from Blake’s mythology who has long white hair and beard: above is Elohim Creating Adam from 1795, for example.

In Blake’s God Judging Adam also from about 1795, both figures sport long, flowing white hair and beards, which appear to be markers not so much of their ages or identities, but of the ancient nature of events.

Long white hair and beards are of course a long-established tradition in visual art: here is a contemporary example of Tiresias, the blind prophet of Apollo at Thebes, in Henry Fuseli’s Tiresias Appears to Ulysses During the Sacrifice (1780-85). Fuseli was Professor of Painting at the Royal Academy, and a great influence on Blake.
Characteristic of the figure of Urizen in The Ancient of Days is the unusual way in which the figure’s hair and beard stream as if in a strong wind, the figure’s nakedness, and its posture.

This can be traced most immediately to a major work by another contemporary painter who was highly influential on Blake: James Barry’s King Lear Weeping over the Dead Body of Cordelia (1786–8). Barry was also Professor of Painting at the Royal Academy, and the similarities between King Lear’s white hair and beard here, and those of Blake’s Urizen in The Ancient of Days, are striking.

Blunt found another potential source in Pellegrino Tibaldi’s figure of Neptune (1549-51) in his fresco showing the story of Ulysses in the Palazzo Poggi. Although now relatively obscure, Blake saw fresco as being ‘true’ art, and was long an enthusiast of frescos, even if he saw few. A contemporary popular book of prints of frescos included an engraving of Tibaldi’s Neptune, so this image would have been accessible to both Blake and Barry.

It’s likely that Tibaldi’s Neptune was itself a reference to Michelangelo’s earlier frescos in the Sistine Chapel: the detail above showing God creating the sun and moon, and even more important that below showing the creation of Adam (c 1511).

Blake knew both of these sections of the Sistine Chapel frescos well, having engraved them previously. They also link to Blake’s own Elohim Creating Adam above.
Blake’s Urizen the architect, seen creating the world using his compasses, is distinct from both God and Elohim in his nakedness. In Blake’s written narrative, the distinction between Urizen and Elohim becomes more blurred, when the former goes on to have the role of the jealous and vengeful god of the Old Testament, until his desire for dominion brings about his downfall.
It may be tempting to assume that, just because Blake’s paintings appear so original and different, they originate entirely from his own mind. However, Blake was just as likely to borrow from and refer to other visual art as any other master.
References
Blunt, A (1959) The Art of William Blake, Oxford UP.
Butlin, M (1981) The Paintings and Drawings of William Blake, 2 vols, Yale UP. ISBN 978 0 300 02550 7.
Damon, S Foster (2013) A Blake Dictionary, the Ideas and Symbols of William Blake, updated edn., Dartmouth College Press. ISBN 978 1 61168 443 8.
Vaughan, William (1999) William Blake, British Artists, Tate Publishing. ISBN 978 1 84976 190 1.

Reading Visual Art: 225 Dice
Dice have been thrown and rolled by people throughout recorded history. Early dice were often irregular, made of bone or stone and used primarily for gambling. Although they don’t appear to have played any significant role in mythology, their role in the events of the Crucifixion is well recorded in the New Testament Gospels.

Jacopo Tintoretto’s huge and magnificent Crucifixion from 1565 depicts the biblical account of guards gambling with dice, as seen in the detail below.

On the left of this detail, two men are gambling with dice in a small rock shelter suggestive of a tomb. To the right of them, a gravedigger has just started his work with a spade.
The Christian Church has generally taken a dim view of gambling, and shunned dice. Several artists have expressed this in paint, but none so forcefully as Hieronymus Bosch.

For Bosch and his patrons, gambling was definitely one of the cardinal sins. It appears in the garden of Hell in the right panel of his magnificent Garden of Earthly Delights from about 1495-1505. In this damning conclusion, figures are mutilated and tormented in a nightmare landscape dominated by non-human creatures and alarming objects, where gambling takes the foreground.

In the foreground, a huge blue bird, wearing a cauldron on its head and swallowing a whole human, presides over the scene. Two main groups of victims here are clustered around objects associated with gaming and gambling, and those for making music, then associated with the work of the devil, and immoral activities such as dancing. Playing cards are scattered on the ground beneath an overturned gaming table, and dice are balanced precariously on an index finger and on the head of a naked woman. From among that cluster of figures, a pair of dark blue non-human arms holds high a backgammon board with three dice.
Not all cultures have been as damning, though.

Albert Anker turns to classical times to put his spin on gambling games in his Knucklebone Players (1864). Three youths are here playing a game of greater skill, but still frequently the basis of gambling. The distinguished men in the background are hardly the sort to frequent a gambling den, surely?
Nevertheless, playing with dice continued as a cardinal sign of sin.

Alcohol, and that other vice tobacco, are cited in David Teniers the Younger’s The Dice Shooters (1630-50). In common with other paintings of card-playing and gambling in this period, it’s set in a dingy room in a rough tavern. Drawing on their clay pipes and with glasses of beer in hand, a group of men are completely absorbed in gambling large stacks of coins on the throw of their dice.

William Hogarth’s successor Thomas Rowlandson painted his Gaming Table in watercolour in 1801. Players here are putting their stakes on dice, which are about to be revealed by the man at the far right of the table, who seems to have been raking the money in.

Although Gustave Courbet’s portrait of the operatic singer Louis Gueymard (1822–1880) as Robert le Diable (1857) shows the last scene in Act 1 of Giacomo Meyerbeer’s opera Robert le Diable (1831), its message is clear. In this scene, Gueymard’s character Robert gambles away his entire estate on dice; in the opera this is marked by the aria L’or est une chimère: ‘gold is but an illusion’.

Dante Gabriel Rossetti is less convinced. In his watercolour of Hesterna Rosa from 1865, he shows a moment from the contemporary play Philip van Artevelde, written by Henry Taylor in 1834. Van Artevelde was a Flemish patriot who lived between about 1340-1382, and led the Ghent rebellion in 1381, only to be crushed to death in battle the following year. In Taylor’s play, van Artevelde has a relationship with a woman of lower class; in this scene, his lover has paused to reflect on her life while he plays dice with a friend. The painting’s title means yesterday’s rose, and draws on the theme of the fallen woman, so popular with Rossetti.

Murillo’s Young Boys Playing Dice of about 1675 adopts a different theme, of gambling among the poor and vulnerable. Two scruffy urchins from the streets of Seville are throwing dice for the small piles of change which are probably their entire fortunes. A third stands over them, chewing at a bread roll while his dog looks up longingly at the food.

使用 Meilisearch 提升 Halo 网站的搜索体验
Halo 从 2.0 版本开始支持了全文搜索功能,自带的 Lucene 搜索引擎在轻度使用场景下可以满足需求,但在重度依赖搜索功能的场景下,可能在搜索速度和用户体验上存在不足,这时我们会更推荐使用独立的搜索引擎。
本文将介绍如何使用
搭建 Meilisearch 服务
部署 Meilisearch 服务通常有两种方式:你可以选择自行在服务器上托管,或者使用 Meilisearch 官方提供的云服务。
云服务
访问
需要特别注意 Meilisearch 云服务的计费方式。
自托管
下面介绍两种常见的部署方式:
使用 Docker Compose 独立编排部署
这种方式适合多个项目需要同时使用一个 Meilisearch 服务的场景。 部署完成后,你可以配置域名和反向代理来暴露服务到公网。
services:
meilisearch:
image: getmeili/meilisearch:v1.15
restart: unless-stopped
ports:
- "7700:7700"
environment:
- MEILI_ENV=production
// [!code highlight]
- MEILI_MASTER_KEY=<your-super-secret-master-key-here>
volumes:
- meilisearch_data:/meili_data
volumes:
meilisearch_data:
driver: local
与 Halo 的 Compose 编排一起部署
结合 使用 Docker Compose 部署 Halo 的示例,将 Meilisearch 服务添加到
docker-compose.yml文件中。
通过这种方式部署之后,插件设置中的 Meilisearch 服务地址 应该是
http://meilisearch:7700(即服务在同一 Compose 网络下可通过服务名访问)
meilisearch:
image: getmeili/meilisearch:v1.15
restart: on-failure:3
networks:
- halo_network
volumes:
- ./meilisearch-data:/meili_data
environment:
- MEILI_ENV=production
// [!code highlight]
- MEILI_MASTER_KEY=<your-super-secret-master-key-here>
点击查看完整示例
version: "3"
services:
halo:
image: registry.fit2cloud.com/halo/halo:2.21
restart: on-failure:3
depends_on:
halodb:
condition: service_healthy
networks:
halo_network:
volumes:
- ./halo2:/root/.halo2
ports:
- "8090:8090"
healthcheck:
test:
["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
environment:
- JVM_OPTS=-Xmx256m -Xms256m
command:
- --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
- --spring.r2dbc.username=halo
- --spring.r2dbc.password=openpostgresql
- --spring.sql.init.platform=postgresql
- --halo.external-url=http://localhost:8090/
halodb:
image: postgres:15.4
restart: on-failure:3
networks:
halo_network:
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
environment:
- POSTGRES_PASSWORD=openpostgresql
- POSTGRES_USER=halo
- POSTGRES_DB=halo
- PGUSER=halo
// [!code focus:10]
meilisearch:
image: getmeili/meilisearch:v1.15
restart: on-failure:3
networks:
- halo_network
volumes:
- ./meilisearch-data:/meili_data
environment:
- MEILI_ENV=production
- MEILI_MASTER_KEY=<your-super-secret-master-key-here>
networks:
halo_network:
详细的部署方式可以参考 Meilisearch 官方文档:https://www.meilisearch.com/docs/learn/self_hosted/install_meilisearch_locally
安装插件
之前 Halo 社区中已经有人开发了 Meilisearch 插件,但已经不再维护,因此这里我们选择使用 Halo 官方提供的插件。
-
下载插件,目前提供以下两种下载方式:
-
GitHub Releases:访问 Releases 下载 Assets 中的 JAR 文件。
-
安装插件,插件安装和更新方式可参考:https://docs.halo.run/user-guide/plugins
配置插件
-
进入插件设置,配置 Meilisearch 服务地址 和 Master Key。索引名称 可以选择使用默认的
halo或者自定义(如果你的 Meilisearch 服务会被多个项目使用,建议自定义索引名称)。
-
进入插件扩展配置,在 扩展点定义 中选择 搜索引擎,然后选择使用 Meilisearch。

数据概览
配置完插件后,我们可以进入插件的 数据概览 页面,查看 Meilisearch 的索引数据。
![]()
在这个页面中,你还可以重建索引或测试搜索功能。
![]()
对比
Lucene(默认搜索引擎)与 Meilisearch 的实际对比:
|
Meilisearch |
Lucene(默认) |
|---|---|
|
|
|
|
|
|
通过实际使用可以发现,Meilisearch 的搜索结果更加准确,搜索速度更快,并且支持更灵活的搜索语法,无需用户掌握复杂的搜索表达式即可获得理想的搜索结果。
注意
-
如果配置完 Meilisearch 插件之后无法搜索,可以尝试重建一次索引。
-
安装 Meilisearch 插件之后仍然需要
https://www.halo.run/store/apps/app-DlacW 插件,Meilisearch 插件仅仅是提供服务,不会提供 UI。

在 Halo 中导入 Markdown 和 Word 文档
在 Halo 社区中,导入 Markdown 和 Word 文档的需求一直很高,但社区一直缺乏完善的解决方案。其主要原因在于 Markdown 和 Word 的文档格式较为复杂,难以完美支持所有格式特性,且图片资源的处理存在技术难点。
现在,社区中已经有了一个插件可以很好地支持导入 Markdown 和 Word 文档,它就是
安装
可以通过以下两种方式安装插件:
-
在 Console 内置的应用市场中搜索 内容助手 进行安装
![]()
导入 Markdown 文档
安装并启用插件后,就可以在 Console 侧边菜单的工具中找到 文章导入 的入口。点击进入后,选择 Markdown 导入 选项卡即可开始导入,如下图:
![]()
功能详解
-
选择 Markdown 文件:用于选择单个 Markdown 文档,支持
.md格式文件。 -
选择 Markdown 文件夹:用于选择包含 Markdown 文档的文件夹。选择文件夹后,系统会自动扫描其中的所有 Markdown 文档以及图片资源(如有)。
-
选择图片文件夹:用于选择 Markdown 文档中引用的图片资源。选择文件夹后,系统会自动扫描其中的所有图片资源并在导入时自动关联。
-
转为富文本格式:默认情况下,导入的 Markdown 文档会保持原有的 Markdown 格式。如果勾选此选项,系统会将文档转换为富文本格式,便于后续使用 Halo 的默认编辑器进行编辑。
使用场景
-
从其他博客平台或写作工具迁移文章内容
-
导入使用本地 Markdown 编辑器创作的文章
-
批量导入历史文档和资料
注意事项
-
导入 Markdown 文档后,如果需要在 Console 中编辑文章,请确保已经安装了任意一个 Markdown 编辑器插件,否则无法正常打开编辑页面。
-
如果 Markdown 文档中引用了本地图片资源,请在导入前选择存放图片的文件夹,否则图片将无法正确上传和关联。
-
系统支持自动解析 Front Matter(文档头部的元数据),包括标题、别名(slug)、描述、摘要、分类、标签等信息。
操作示例
选择 Markdown 文件:
![]()
选择图片文件夹(如果文档包含本地图片):
![]()
点击导入,等待导入完成:
![]()
检查文章与图片资源是否导入成功:
![]()
![]()
导入 Word 文档
进入 文章导入 页面后,选择 Word(.docx)导入 选项卡,如下图:
![]()
功能详解
-
选择 Word 文档:用于选择单个 Word 文档,支持
.doc和.docx格式。 -
选择 Word 文档文件夹:用于批量选择包含 Word 文档的文件夹。
-
转为 Markdown 格式:默认情况下,导入的 Word 文档会转换为富文本格式。如果希望后续使用 Markdown 编辑器编辑文章,请勾选此选项将内容转换为 Markdown 格式。
使用场景
-
将公司内部的 Word 文档转换为博客文章
-
配合
https://maxkb.cn/ 智能知识库,建立企业知识管理体系 -
从传统文档工具迁移内容到现代化的管理平台
注意事项
-
由于 Word 文档格式的复杂性,系统可能无法完美解析所有内容格式,建议导入后进行适当调整。
-
系统支持自动导入 Word 文档中的图片资源,但其他类型的嵌入对象暂不支持。
-
图片会上传到与个人中心关联的存储策略,请提前在用户设置中配置相关参数。
操作示例
选择 Word 文档:
![]()
点击导入,等待导入完成:
![]()
检查文章是否导入成功:
![]()
![]()
丰富的内容管理功能
除了核心的导入功能,内容助手还提供了丰富的内容管理功能:
格式转换
支持 Markdown 与富文本格式的双向转换,让用户可以根据编辑需求灵活切换文档格式。你可以在文章管理页面点击文章的 ··· 按钮,在转换菜单中选择相应的格式转换选项,也可以在文章编辑页面顶部的编辑器选择框中选择 内容格式转换器 进行转换。
![]()
文章导出
支持将文章导出为多种格式,方便内容备份和分享:
-
以原格式导出:保持文章的原始格式进行导出
-
转换为 Markdown 并导出:将文章转换为 Markdown 格式后导出
-
转换为 PDF 并导出:将文章转换为 PDF 格式进行导出
![]()
文章克隆
提供文章克隆功能,便于基于现有文章创建相似内容。克隆后的文章会自动在标题后添加"(副本)"标识,并生成新的别名以避免冲突。
![]()
以上功能都可以在文章管理页面点击文章的 ··· 按钮找到相应选项。
总结
内容助手插件为 Halo 用户提供了完整的文档导入和内容管理解决方案,有效解决了 Markdown 和 Word 文档的导入难题。插件不仅支持智能处理图片资源,还提供了格式转换、文章导出、文章克隆、内容复制等丰富功能,能够满足大部分用户的内容管理需求。
无论你是从其他平台迁移内容,还是需要批量导入历史文档,内容助手都能为你提供便捷、高效的解决方案。如果你有文档导入或内容格式转换的需求,欢迎尝试使用内容助手插件。

使用 Rspack 构建 Halo 插件的前端部分
更新(25-06-19)
现在已经为插件的 UI 部分提供了新的配置方式,rsbuildConfig 方法,可以更加方便的使用
Rsbuild 基于 Rspack 构建,提供了更完善的 loader 配置,所以在封装的时候就直接选择了 Rsbuild。
安装依赖:
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D
rsbuild.config.mjs:
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
export default rsbuildConfig()
package.json 添加 scripts:
{
"type": "module",
"scripts": {
"dev": "rsbuild build --env-mode development --watch",
"build": "rsbuild build"
}
}
需要注意的是,为了适应新版的 ui/build/dist ,如果你要从已有的插件项目迁移到 Rsbuild,建议参考
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
const OUT_DIR_PROD = "../src/main/resources/console";
const OUT_DIR_DEV = "../build/resources/main/console";
export default rsbuildConfig({
rsbuild: ({ envMode }) => {
const isProduction = envMode === "production";
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
return {
resolve: {
alias: {
"@": "./src",
},
},
output: {
distPath: {
root: outDir,
},
},
};
},
});
示例:
了解更多:
前情提要
Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。 基于这个前提,在实现插件机制时,主要面临的问题就是如何将这个大对象传递给 Halo。当初做了非常多的尝试,最终选择构建为 IIFE(Immediately Invoked Function Expression,立即执行函数),然后 Halo 通过读取 window[PLUGIN_NAME](PLUGIN_NAME 即插件名)来获取这个对象。 构建方案采用 Vite,并提供了统一的构建配置。回过头来看,这个方案存在不少问题:
-
会污染 window 对象,虽然目前并没有出现因为这个导致的问题,但是从长远来看,这个方案并不是最优的。(当然,使用 Rspack 来构建并不是为了解决这个问题)
-
Vite 不支持 IIFE / UMD 格式的代码分割(主要是 Rollup 还不支持),无法像 ESM(ECMAScript Module)那样实现异步加载模块的机制。
-
基于第 2 点,如果插件中实现了较多的功能,可能会导致最终产物体积巨大,尤其是当用户安装了过多的插件时,会导致页面加载缓慢。
-
以
http://www.halo.run/ 为例,gzip 之前接近 10M 的 bundle.js,gzip 之后也有 2M - 3M。 -
以此博客为例,gzip 之后也有 1.8M 的 bundle.js。
-
-
基于第 2 点,如果不支持代码分块(Chunk),也无法充分利用资源缓存,访问页面时,也会一次性加载所有插件的代码(即便当前页面不需要)。
基于以上问题,我开始寻找其他替代方案,最终通过翻阅 Rspack(Webpack 的 Rust 实现)的文档发现,Webpack 能够通过配置实现 IIFE 格式的代码分割,最终选择 Rspack 作为尝试。
基本的 Rspack 配置
安装依赖:
pnpm install @rspack/cli @rspack/core vue-loader -D
package.json 添加 scripts:
{
"type": "module",
"scripts": {
"dev": "NODE_ENV=development rspack build --watch",
"build": "NODE_ENV=production rspack build"
}
}
rspack.config.mjs:
import { defineConfig } from '@rspack/cli';
import path from 'path';
import process from 'process';
import { VueLoaderPlugin } from 'vue-loader';
import { fileURLToPath } from 'url';
// plugin.yaml 中的 metadata.name
const PLUGIN_NAME = '<YOUR_PLUGIN_NAME>';
const isProduction = process.env.NODE_ENV === 'production';
const dirname = path.dirname(fileURLToPath(import.meta.url));
// 开发环境启动直接输出到插件项目的 build 目录,无需重启整个插件
// 生产环境输出到插件项目的 src/main/resources/console 目录下
const outDir = isProduction ? '../src/main/resources/console' : '../build/resources/main/console';
export default defineConfig({
mode: process.env.NODE_ENV,
entry: {
// 入口文件,可以参考:https://docs.halo.run/developer-guide/plugin/basics/ui/entry
main: './src/index.ts',
},
plugins: [new VueLoaderPlugin()],
resolve: {
alias: {
'@': path.resolve(dirname, 'src'),
},
extensions: ['.ts', '.js'],
},
output: {
// 资源根路径,加载代码分块(Chunk)的时候,会根据这个路径去加载资源
publicPath: `/plugins/${PLUGIN_NAME}/assets/console/`,
chunkFilename: '[id]-[hash:8].js',
cssFilename: 'style.css',
path: path.resolve(outDir),
library: {
// 将对象挂载到 window 上
type: 'window',
export: 'default',
name: PLUGIN_NAME,
},
clean: true,
iife: true,
},
optimization: {
providedExports: false,
realContentHash: true,
},
experiments: {
css: true,
},
devtool: false,
module: {
rules: [
{
test: /\.ts$/,
exclude: [/node_modules/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
},
},
},
type: 'javascript/auto',
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
],
},
// 这部分依赖已经由 Halo 提供,所以需要标记为外部依赖
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
'@vueuse/core': 'VueUse',
'@vueuse/components': 'VueUse',
'@vueuse/router': 'VueUse',
'@halo-dev/console-shared': 'HaloConsoleShared',
'@halo-dev/components': 'HaloComponents',
'@halo-dev/api-client': 'HaloApiClient',
'@halo-dev/richtext-editor': 'RichTextEditor',
axios: 'axios',
},
});
配置需要懒加载的路由或者组件:
在 index.ts 中配置路由:
import { definePlugin } from '@halo-dev/console-shared';
import { defineAsyncComponent } from 'vue';
import { VLoading } from '@halo-dev/components';
import 'uno.css';
// [!code --]
import DemoPage from './views/DemoPage.vue';
export default definePlugin({
routes: [
{
parentName: 'Root',
route: {
path: 'demo',
name: 'DemoPage',
// [!code --]
component: DemoPage,
// [!code ++:4]
component: defineAsyncComponent({
loader: () => import('./views/DemoPage.vue'),
loadingComponent: VLoading,
}),
...
},
},
],
extensionPoints: {},
});
注:推荐使用 defineAsyncComponent 包裹,而不是直接使用 () => import() 的方式,后者会在进入路由之前就开始加载页面的代码分块(Chunk),导致页面在加载期间没有任何响应。
构建产物示例:
❯ ll src/main/resources/console
.rw-r--r-- 191k ryanwang staff 16 Jun 10:47 359-3bebb968.js
.rw-r--r-- 83k ryanwang staff 16 Jun 10:47 962-3bebb968.js
.rw-r--r-- 4.1k ryanwang staff 16 Jun 10:47 main.js
其他配置
集成 Scss / Sass
安装依赖:
pnpm install sass-embedded sass-loader -D
rspack.config.mjs 添加配置:
import { defineConfig } from '@rspack/cli';
import path from 'path';
import process from 'process';
import { VueLoaderPlugin } from 'vue-loader';
import { fileURLToPath } from 'url';
// [!code ++]
import * as sassEmbedded from "sass-embedded";
...
export default defineConfig({
...
module: {
rules: [
...
// [!code ++:13]
{
test: /\.(sass|scss)$/,
use: [
{
loader: "sass-loader",
options: {
api: "modern-compiler",
implementation: sassEmbedded,
},
},
],
type: "css/auto",
},
],
},
...
});
集成 UnoCSS
如果你习惯使用 TailwindCSS 或者 UnoCSS 来编写样式,可以参考以下配置:
本文推荐使用
https://unocss.dev/ ,因为可以利用 UnoCSS 的https://unocss.dev/transformers/compile-class 来编译样式,预防与 Halo 或者其他插件产生样式冲突。
安装依赖:
pnpm install unocss @unocss/webpack @unocss/eslint-config style-loader css-loader -D
入口文件(src/index.ts)添加导入:
import 'uno.css';
rspack.config.mjs 添加配置:
import { defineConfig } from '@rspack/cli';
import path from 'path';
import process from 'process';
import { VueLoaderPlugin } from 'vue-loader';
import { fileURLToPath } from 'url';
// [!code ++]
import { UnoCSSRspackPlugin } from '@unocss/webpack/rspack';
...
export default defineConfig({
...
plugins: [
new VueLoaderPlugin(),
// [!code ++]
UnoCSSRspackPlugin()
],
...
module: {
rules: [
...
// [!code ++:5]
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
type: 'javascript/auto',
},
],
},
...
});
uno.config.ts:
import { defineConfig, presetWind3, transformerCompileClass } from 'unocss';
export default defineConfig({
presets: [presetWind3()],
transformers: [transformerCompileClass()],
});
.eslintrc.cjs:
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution');
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-recommended',
'eslint:recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier',
// [!code ++]
'@unocss',
],
env: {
'vue/setup-compiler-macros': true,
},
// [!code ++:3]
rules: {
"@unocss/enforce-class-compile": 1,
},
};
总结
以上就是针对 Halo 插件前端部分的 Rspack 配置。我已经对 Halo 官方维护的部分插件进行了迁移,几乎没有遇到什么问题,并且带来的收益非常明显:www.halo.run 和本博客的 bundle.js 在 gzip 之后仅有不到 200k,各个页面也只会在访问时加载所需的资源。
需要注意的是,我对这些构建工具并不算非常熟悉,所以配置仍然有优化空间。我们会持续优化,后续也会考虑提供一个通用的 CLI 或 Rspack 配置,期望实现如下效果:
rspack.config.mjs:
import { rspackConfig } from '@halo-dev/ui-bundler-kit';
export default rspackConfig({
...
});
或者基于 Rspack 包装一个 CLI:
plugin.config.mjs:
import { defineConfig } from '@halo-dev/ui-bundler-kit';
export default defineConfig({
...
});
package.json:
{
"scripts": {
"dev": "halo-ui dev",
"build": "halo-ui build"
}
}
参考文档
感谢阅读,欢迎交流与指正!

读完一本不好看的书,但心里很舒坦


在西西弗里偶遇这本书,随手翻了一下,被设定吸引了,就一下看了前九章。
二十三天后回到书店里把余下的二十二章看完了,满足的同时又觉得很失望。
满足的是,这个下午是我近一年来完整读完了一本书的时刻;失望的是,前半截一直吊着我胃口的摆渡世界的故事,最后居然演变成了俗气的爱情故事和死而复生的怪诞情节。我不喜欢这样的收尾。
但是,迪伦凭着自己的信念从死亡的世界回到人间这段路,这一路的勇气,是我愿意把第三颗星打上来的原因。书里的男女角色我都不怎么喜欢,无辜枉死的三十六岁女士也很莫名其妙,但对于此刻低气压的我而言,我喜欢迪伦一路冲过去的那份勇气和冒险的决心。
对多数人而言,读这本书是浪费时间。但我之所以感觉还行,是因为我太久没有体会到「完成」一件事时「结束」的那一刻了。哪怕这一刻并不欢欣鼓舞,但我完成了。




相对应的,前两天看完的两部片子,让我感到心里非常的舒坦。一个是贾玲的新电影《热辣滚烫YOLO》,另外一个是 Casey 最新的一条 vlog《Sisyphus and the Impossible Dream》。
一方面惊叹于贾玲真的一年瘦下来一百斤,练成了可以和职业拳击运动员打几下的状态;二来佩服于她为了实现这个目标所做的一切努力,一切向生活挥拳而做的事情。她不是瘦了,而是变了一个人,瘦下来只是一个副产品。
Casey 的 vlog 时间跨度长达 17 年。从大腿骨折,到跑进三小时以内,从二十来岁到四十多,一切的付出,就像西西弗斯一次次推石头上山,不仅过程令我震动,结果更是让我感受到了希望!
他俩是我 2024 年初的第一束光。

我的 2023
关于工作
今年是全职维护 Halo 的第二年, 也是 Halo 快速成长的两年,这两年 Halo 的成长可以说比以前任何时间段都要快(虽然一共才 5 年),这如果要放在以前非全职的时候,当前 Halo 的状态我们可能花个三四年都达不到,虽然目前 Halo 依旧有很多可优化的空间。
关于 Halo 2023 年的一些总结:
-
一共发布了 12 个版本,截止到当前编写此文,即将发布 2.12。
-
Star 达到 30k。
-
终于上线了可用的应用市场,包括内置版,用户可以非常方便的下载和更新插件/主题。
-
应用市场支持付费应用,这为我们带来了一部分收益,但远远没有达到养活我们自己的地步。
-
插件和主题数量得到提升,也有了越来越多的社区开发者。
今年的一些规划:
-
应用市场支持开发者入驻,包括发布付费应用。
-
通过第一点,继续推进 Halo 的生态发展。
-
尝试一些其他的商业模式,争取达到一个正向循环。
-
对 Halo 本身的功能和稳定性进行持续优化。
-
重视文档。
虽然 Halo 需要推进一些商业模式来得以存活,但我们最大的坚持就是保证 Halo 本身的纯净和优雅,可以从我们应用市场看出,即便是我们提供了服务的应用市场内置版本,也是通过插件的形式集成到 Halo 的,如果用户不喜欢,完全可以直接卸载,不会有任何影响。
一些反思,以下是我们需要一直反思的事情:
-
Halo 的定位和面向群体。
-
Halo 是否真的满足了建站的需求,还是仅仅是内容管理。
-
基于第二点,为什么 Slogan 是突出的建站,但我们直到 2.12 还是没有针对页面进行一些有用的功能开发,比如可视化编辑网页内容。
-
可持续的商业模式。
好了,以上是关于 Halo 的一些总结和反思,以下是我个人的一些工作终结,直接看图吧。
![]()
来年对我自己的期望:
-
继续深耕技术,需要对巩固很多基础知识。
-
改变对产品某些地方的思维方式,尝试一些新的东西。
-
提高效率,需要加强对工作优先级的判断。
-
开始了解一些运营和商业的知识,对于一个做技术的人来做,这一步真的很难迈出。
关于生活
依旧蓉飘中,这应该是是在成都的第 5 年了,成都虽然比不上北上广深那样的快节奏,但这主要还是分人吧,这两年基本上都投入到工作上了。
生活依旧很平淡,希望今年可以稍微改变一下。
今年最大的事应该就是结婚啦~
一些分享
在这里分享一下在 2023 年用过的一些好软件、好的日用产品等。
App
1. Raycast
Raycast 其实已经用了很久了,从最开始发布的时候就一直在用,已经成为了日常必备,不管是用来启动 App,还是利用其中的插件来完成一些其他 App 的集成,都非常好用。尤其是这个产品的细节,非常打动我,这个产品是我学习的对象。
贴一张来自 Raycast 的年终使用报告吧。
![]()
2. OrbStack
一个 Docker 以及 Linux 虚拟机的管理软件,非常轻量,没有官方 Docker Desktop 那样臃肿和那么多问题。这目前也是我的日常必备了,尤其是 Linux 虚拟机,如果我需要在 Linux 上测试一些东西,直接创建一个新的 Linux 实例即可,非常快速。
开源软件/库
1. TanStack Query
一个非常好用的数据状态管理库,在此之前,我在页面上获取数据基本都只是通过 Axios 请求,然后手动管理数据的状态,一旦页面复杂一点,数据的获取和管理将会比较复杂,代码也不够简洁。用了这个库之后,可以非常容易的做到:
-
简化数据请求的逻辑,可以使用声明式(配置式)的写法完成数据的请求和管理。
-
数据缓存、数据共享(组件之间)、重复请求去重。
-
监听设备网络状态、窗口聚焦监听。
-
请求重试,支持自行编写逻辑。
-
定时请求,支持自行编写逻辑,这对异步接口(需要定时重新刷新数据)非常友好。
-
分页和滚动加载。
-
依赖请求。
类似的库还有
2. VueUse
来自 Vue 和 Vite 的核心开发人员
-
useLocalStorage:通常用于在浏览器临时保存一些用户偏好设置。
-
useRouteQuery:通常用于在地址栏保存数据列表的筛选项,方便在用户切换路由或者刷新页面之后可以回到之前的查询状态。
-
useFileDialog:用于简化上传文件的逻辑。
-
useEventListener:再也不用担心在组件 unmounted 的时候取消注册事件。
非常钦佩 Anthony Fu 在开源社区做出的贡献,尤其是 Vue 和 Vite 生态。
3. unplugin-icons
同样来自
如果你使用 Tailwindcss,也可以尝试使用 tailwindcss-plugin-icons,同样依赖于 Iconify,这个库可以将 SVG 编译到最终的 CSS 产物中。这个库会在我写 Halo 主题的时候用到。
好物
1. Huadn 荞麦舒颈枕
近两年饱受颈椎问题困扰,经常会感到颈椎痛和甚至头晕,严重的时候早上起床之后都无法低头,一低头就非常刺痛。已经记不清去了多少次医院了,中医和西医都去过,还做了好几次针灸,但都没有根本解决问题,一段时间之后又会复发。于是开始怀疑是枕头的问题,于是在京东上东找西找发现了这个枕头,已经使用了大概五个月了,已经完全没有再颈椎痛过,只是偶尔会在久坐之后感到颈椎稍微有点僵硬。
原来这么久的颈椎问题是因为睡眠!可能是因为之前的枕头睡着会让颈部悬空,而这个枕头会刚好拖住我的颈椎。
如果你也有颈椎问题,不妨试着换个枕头。
2. 佳能 EOS RP 相机
在此之前,我一直以为我的 iPhone 拍照已经足够好,完全不需要相机,但购买之后发现相机拍出的质感是手机不能比的。不过 23 年没有太多机会出去玩,希望今年可以有空多出去走走。
3. 徕芬吹风机
非常佩服这家公司的产品,这个吹风机颜值高,转速快,可以让我早上早出门两分钟。
以上就是我 2023 的一些总结,希望 2024 更好!

新起点丨开源建站工具 Halo 发布 2.0 版本
2022 年 12 月 1 日,FIT2CLOUD 飞致云旗下开源建站项目 Halo( github.com/halo-dev )正式发布 v2.0 版本。这是 Halo 项目继 v1.0 版本后的第二个里程碑版本,研发团队采用全新架构进行项目重写,实现了从单用户机制向多用户体系的转变,提供全新设计的插件机制和主题机制,改进了附件管理方式,为用户提供富文本编辑器,同时提供后台全局搜索能力。
Halo 是一款好用又强大的开源建站工具,它让你无需太多的技术知识就可以快速搭建一个博客、网站或者内容管理系统。
截至目前(2022 年 12 月 1 日),Halo 已经在 Docker Hub 获得了超过 150 万次下载,GitHub Star 数突破 24 k,并拥有一百多名社区贡献者。我们在此对所有参与到 Halo 产品及社区建设的朋友们表达由衷的感谢。
主要亮点功能
多用户与权限体系
Halo 1.0 版本仅支持单管理员机制,这极大限制了 Halo 的多人使用场景。在 Halo 2.0 中,我们引入了多用户及 RBAC 权限体系,支持多用户同时登录管理 Halo,并且支持用户权限的细粒度控制,可以为不同的用户分配不同的权限,从而实现不同的用户角色。
![]()
灵活可扩展的插件机制
Halo 2.0 带来了全新设计的插件机制,这也是 2.0 版本底层架构变动的主要原因。在 Halo 1.0 时,我们因为无法对功能进行拓展,所以随着版本不断迭代会导致系统越来越臃肿。比如我们在 1.0 集成了市面上常见的云存储方案,但绝大部分用户都不会使用到所有的存储方案,这些功能对于 Halo 来说就变成了一种负担。在 Halo 2.0 中,我们将这些功能抽离出来,通过插件的形式进行集成,这样用户可以根据自己的需求自由选择,不再会因为一些不需要的功能而导致系统的臃肿。同时也可以实现不更新整个 Halo 应用,对插件进行单独更新,降低用户更新使用新功能的代价。
我们为插件机制提供了以下能力:
- 插件的动态加载、卸载、升级;
- 自定义模型的接入,方便插件的数据存储;
- 接入 Halo 的配置模块,方便插件的配置管理;
- 以及拓展后端、管理端、主题端的能力。
![]()
从上图可以看到:
- 我们将评论组件和搜索组件做成了插件,这是为了方便主题直接使用,而不需要主题单独为这两个功能进行开发;
- 1.0 的友情链接管理功能也被抽离出来,成为了一个独立的通用插件;
- 阿里云 OSS 的存储策略也使用插件提供,安装了这个插件即可将附件上传至阿里云 OSS;
- Umami 插件提供了对 Umami 的集成,可以在 Halo 控制台直接查看网站访问情况;
- Unsplash 插件提供了对 Unsplash 的集成,可以在编写内容的时候从 Unsplash 选择图片。
相信随着我们对 Halo 的持续迭代和生态建设的持续投入,Halo 的插件生态会越来越丰富。
目前已支持 Halo 2.0 的插件可以访问 https://github.com/halo-sigs/awesome-halo 查阅。
全新的主题机制
Halo 2.0 在主题机制上,有以下主要改进:
- 使用 Thymeleaf 作为默认的模板引擎;
- 支持主题预览,可以在不启用主题的情况下预览主题效果;
- 支持多语言;
- 全新的设置表单定义机制,支持更多的输入选项,支持表单验证和条件判断;
- 全新设计的主题可视化设置界面,支持预览不同设备的效果,支持保存设置之后实时预览效果。
![]()
全新的默认主题
我们为 Halo 2.0 提供了全新的默认主题,并命名为 Earth,我们计划在未来以太阳系成员的命名方式提供一系列的官方默认主题。
![]()
可扩展的附件管理
在 Halo 1.0 时,社区用户呼声比较高的需求之一就是改进附件管理方式。在 2.0 版本,我们全新设计了附件的功能,支持分组管理、存储策略等功能。分组管理功能可以帮助用户更好地组织管理不同使用场景的附件。存储策略功能可以让用户定义多个不同的附件存储位置,同时也可以通过插件来拓展外部云存储,比如上面提到的阿里云 OSS。
![]()
![]()
![]()
此外,选择附件时还可以通过插件支持更多的附件来源,比如上面提到的 Unsplash 插件,可以做到在编辑内容的时候直接从 Unsplash 网站选择配图。
![]()
强大的富文本编辑器
在 2.0,为了解决 Markdown 编辑器无法对一些复杂的排版场景进行支持的问题,我们默认提供了富文本编辑器,它能够很好地支持图片插入、表格、任务列表等。不过考虑到部分用户对 Markdown 有较强的需求,我们将在后续版本中提供对 Markdown 格式编辑的支持。除此之外,你也可以通过安装插件的方式使用你最喜欢的编辑器。
![]()
便捷的后台全局搜索
我们在后台提供了全局搜索功能,可以快速搜索后台的页面、文章、附件、主题等资源。并且提供了快捷键(Ctrl + K,Mac 下为 Cmd + K)来快速打开搜索框,提高后台的操作效率。
![]()
支持搜索引擎
在 Halo 1.0 的时候,我们搜索文章是通过 SQL 模糊匹配的方式来实现的,这种方式的搜索效率和准确性都不够理想。在 2.0 中,我们默认使用了 Apache Lucene 来提供搜索引擎的支持,也为主题端提供了通用的搜索框组件,大大提高了搜索功能的实用性。同样的,搜索引擎我们也支持通过插件来拓展,比如集成 Elasticsearch、MeiliSearch 等。
![]()
完整变更日志
关于 Halo 2.0 的完整特性和变更日志,请访问 GitHub Release 页面(https://github.com/halo-dev/halo/releases)。
接下来
在 Halo 2.0 正式版发布之后,我们会继续完善 Halo 的功能和文档,并在每个月按时发布一个功能版本。同时将持续投入对 Halo 生态的建设,让用户能够更加方便的使用 Halo 搭建各式各样的网站,构建心中的理想站点。此外,我们也将在不久之后开启应用市场的开发,让用户获取主题、插件更加便捷。同时我们也会不断加大对前沿技术及用户体验的探索,让 Halo 朝着好用又强大的零代码建站工具的目标持续迈进。
Halo 此前的成绩离不开每一位参与者的贡献与支持,踏上这个新的起点,Halo 的未来也仍需各位共同努力。

Halo 1.5.0 发布
距离我们 2020 年 9 月 24 号发布 1.4.0 已经过去了 545 天了,期间虽然有一些版本更新,但大多数都是 patch 修复版本。终于,在今天正式发布 1.5.0 版本。其中带来了大量的优化更新,下面为大家简单介绍一些亮点功能,详细更新日志可在本文末尾查阅。
版本亮点
文章表拆分
在此版本中,新增了 contents 表专门用于存储文章内容。因为在以前的版本中,当站点有大量文章的时候会出现明显拖慢页面渲染速度的情况,这是因为在之前的版本中,查询文章列表会将内容字段也包含在其中,这就会导致数据越多就会越慢。所以在这个版本中,原本的 posts 表就不再包含 originalContent 和 formatContent 字段,在列表查询的时候也不会包含。需要注意的是,如果你是从 1.4 版本升级的话,目前我们是没有自动清空这两个字段的内容的。当然,你可以手动清空,以获得更好的效果。
性能对比:
- 版本:1.4.17 vs 1.5.0
- 测试数据:文章数 1000,每篇 4000 字符。
- 测试工具:Apache Benchmark
- 测试平台:MacBook Pro M1 Pro
- 测试参数:
ab -c 100 -n 10000 http://localhost:8090/
1.4.17:
![]()
1.5.0:
![]()
注意:此测试可能并不是特别严谨,仅供参考。
文章保存逻辑修改
目前后台已经修改为直接保存编辑器渲染的 html 内容,保证编写时和最终发布结果完全一致。另外,数学公式和 mermaid 图表已经针对此特性做了优化。数学公式仅需在前台引入 katex css 即可。mermaid 图表会被渲染为 svg 并保存,所以无需再引入 mermaid 依赖。
数学公式可以在前台引入:
<link rel="stylesheet" href="https://unpkg.com/katex@0.12.0/dist/katex.min.css" />
![]()
![]()
![]()
![]()
目前,我们已经统一使用 @halo-dev/markdown-renderer 进行 Markdown 渲染。
编辑器重构
编辑器编辑区域修改为 Codemirror 编辑器,支持 Markdown 语法高亮,浏览文章会更加方便。
![]()
编辑器可以通过快捷键打开图片选择,目前也已经支持多选图片和选择之后直接插入文章。
![]()
预览区域支持代码块高亮。
![]()
支持更多 Markdown 标记语法。
![]()
表格编辑体验优化。众所周知,使用 Markdown 语法编写表格的体验是非常难受的,不仅编写困难,而且格式会非常难以阅读。所以我们使用了一个叫做 mte-kernel 的库来解决这个问题,不仅可以让编辑体验升级,而且会自动格式化表格。
![]()
标签支持设置颜色
在这个版本中,我们添加了对标签设置颜色的功能,灵感来自于 GitHub Issues 的标签颜色。这样可以比较直观地区分不同的标签。
![]()
![]()
文章管理重构
重构了批量操作的逻辑,目前已经不需要提前通过文章状态筛选文章之后才能批量选择。可以任意批量选择之后进行文章状态的批量操作。
![]()
文章列表不再展示回收站的文章,改为单独为回收站提供一个入口。
![]()
文章设置弹窗进行了重构,改为更直观地显示方式,并且可以在弹窗中进行上下篇的切换。可以非常快速的预览文章设置并进行对文章设置的修改。
![]()
附件管理重构
目前支持将鼠标放在附件项即可显示勾选图标,不再需要提前进入批量管理功能。
![]()
附件详情也和文章设置一样,修改为弹窗的形式,可以进行上下项的快速切换,方便预览以及进行修改删除等操作。
![]()
主题设置重构
主题设置由原来的全屏抽屉形式改为单独的页面。并优化了布局,不再显示主题缩略图,将更多的空间留给设置表单。同时在界面上也展示了主题的相关信息。
![]()
![]()
版本日志
Breaking changes
-
评论表单中的邮箱地址不再作为必填项。 halo-dev/halo#1535 @jerry-shao
-
重构文章表结构。
- 提供单独的
contents表存储文章内容,将不再使用posts表中的originalContent和formatContent字段。 halo-dev/halo#1617 @guqing formatContent已经废弃,将在下一个大版本中移除。后续使用content字段代替。 halo-dev/halo#1617 @guqing- 提供
content_patch_logs表用于存储变更记录。 halo-dev/halo#1617 @guqing
- 提供单独的
-
后台使用 @halo-dev/admin-api 进行接口请求。 halo-dev/halo-admin#378 @ruibaby @guqing
-
修改后台页面标题后缀,由
Halo Dashboard改为Halo。 halo-dev/halo-admin#426 @ruibaby -
默认后台布局改为左侧菜单布局。 halo-dev/halo-admin#441 @ruibaby
-
重构文章/自定义页面编辑逻辑,改为保存前端渲染内容,放弃后端渲染。 halo-dev/halo-admin#439 halo-dev/halo-admin#440 halo-dev/halo-admin#449 halo-dev/halo#1668 @ruibaby @guqing
-
Content API 的评论列表接口不再返回
ipAddress和email字段。 halo-dev/halo#1729 @guqing
Features
- Content API 添加获取所有图库图片分组的接口(PhotoController#listTeams)。 halo-dev/halo#1515 @fuzui
- Content API 添加根据
themeId获取主题详情和设置的接口。 halo-dev/halo#1660 @guqing - 图库支持点赞。 halo-dev/halo#1537 @guqing
- 文章标签支持设置颜色。 halo-dev/halo#1566 halo-dev/halo-admin#395 @ruibaby @guqing
- 重构后台文章分类管理,支持排序。 halo-dev/halo#1650 halo-dev/halo#1657 halo-dev/halo-admin#435 @lan-yonghui @ruibaby @guqing
- 文章设置界面支持重新生成别名。 halo-dev/halo-admin#368 @ruibaby
- Admin API 提供批量删除图库图片的接口。 halo-dev/halo#1680 @ruibaby
- Admin API 提供批量更新图库图片的接口。 halo-dev/halo#1679 @ruibaby
- 后台评论管理提供日志评论管理的界面。 halo-dev/halo-admin#480 @ruibaby
- 后台菜单分组支持修改分组名。 halo-dev/halo-admin#499 @ruibaby
- 后台个人资料头像修改支持输入链接。 halo-dev/halo-admin#505 @ruibaby
- 后台待审核评论弹框点击评论支持进入评论管理。 halo-dev/halo-admin#517 @ruibaby
- 支持配置 Redis 缓存。 halo-dev/halo#1751 @luoxmc @guqing
Improvements
- 更新 Logo。 halo-dev/halo-admin#366 @ruibaby
- 重构附件上传的文件命名逻辑,文件作为第一次上传的时候文件名不添加随机字符串。 halo-dev/halo#1500 @guqing
- 优化后台编辑文章时的预览体验,预览文章或临时保存内容不会修改已经发布的内容。 halo-dev/halo#1617 halo-dev/halo-admin#439 @guqing @ruibaby
- 重构后台主题设置界面,提供单独的设置页面,支持展示主题的相关信息。 halo-dev/halo-admin#380 @ruibaby
- 弱化后台登录页面的动画效果。 halo-dev/halo-admin#369 @ruibaby
- 优化后台附件管理列表预览图片样式。 halo-dev/halo-admin#374 halo-dev/halo-admin#382 @623337308 @cetr
- 重构后台附件详情界面,取消原有抽屉的设计,改为弹窗。 halo-dev/halo-admin#375 halo-dev/halo-admin#381 @ruibaby
- 重构后台文章/自定义页面设置界面,取消原有抽屉的设计,改为弹窗。 halo-dev/halo-admin#376 @ruibaby
- 重构后台操作日志列表界面,取消原有抽屉的设计,提供单独的页面。 halo-dev/halo-admin#419 @ruibaby
- 重构后台图片选择弹框组件,支持直接插入到编辑器,支持多选。 halo-dev/halo-admin#420 halo-dev/halo-admin#421 @ruibaby
- 后台文章设置选择标签列表改为根据名称排序。 halo-dev/halo-admin#429 @ruibaby
- 优化后台附件管理中批量操作附件的逻辑。 halo-dev/halo-admin#431 @ruibaby
- 后台使用重构版本的编辑器,编辑区域支持高亮语法,优化数学公式和图表等渲染,优化表格的编辑体验。 halo-dev/halo-admin#447 @ruibaby
- 优化文章加密和分类加密的逻辑。 halo-dev/halo#1678 @guqing
- 优化后台登录页面样式。 halo-dev/halo-admin#456 @ruibaby
- 重构后台文章评论列表弹窗。 halo-dev/halo-admin#463 @ruibaby
- 重构后台图库管理页面,支持批量操作图片以及批量从附件库添加图片。 halo-dev/halo-admin#468 @ruibaby
- 重构后台文章管理页面,文章列表将不再展示回收站状态的文章,提供单独的回收站入口。 halo-dev/halo-admin#475 @ruibaby
- 优化后台文章/自定义页面设置的保存逻辑,提供转为发布/草稿的按钮。保存按钮不再影响到文章状态。 halo-dev/halo-admin#476 @ruibaby
- 优化后台折叠菜单的体验,解决折叠时 Logo 和 菜单动画不同步的问题。 halo-dev/halo-admin#493 @ruibaby
- 缓存后台折叠菜单的状态,刷新页面不再会恢复到初始状态。 halo-dev/halo-admin#493 @ruibaby
- 优化后台判断是否初始化的逻辑,修改为每次页面加载只请求一次,切换路由不再请求。 halo-dev/halo-admin#495 @ruibaby
- 重构后台主题设置保存预览功能。 halo-dev/halo-admin#502 @ruibaby
- 重构日志发布功能,使用 Markdown 编辑器替换 textarea,并支持保存前端渲染的 Markdown 结果。 halo-dev/halo#1739 halo-dev/halo-admin#506 @guqing @ruibaby
Bug Fixes
- 修复修改加密文章或分类时没有清除用户访问权限的问题。 halo-dev/halo#1540 @guqing
- 修复重置密码没有校验密码长度的问题。 halo-dev/halo#1636 halo-dev/halo-admin#403 @ruibaby @guqing
- 修复后台文章设置中无法仅选择父级分类的问题。 halo-dev/halo-admin#367 @ruibaby
- 修复后台菜单管理中移动菜单项到其他分组的时候,导致子菜单丢失的问题。 halo-dev/halo-admin#422 @ruibaby
- 更新默认主题的 submodule 提交,修复模板中部分因为数字中带逗号导致的渲染异常。 halo-dev/halo#1682 @ruibaby
- 修复评论默认头像因为修改了默认类型但 options 接口没有返回字段导致评论头像无法显示的问题。 halo-dev/halo#1692 @lan-yonghui
- 修复使用 leveldb 的情况下,解析错误而没有清空缓存导致无法正常使用系统的问题。 halo-dev/halo#1695 @guqing
- 修复后台在文章编辑页面切换左侧菜单收缩的时候出现的样式异常。 halo-dev/halo-admin#465 @ruibaby
- 修复当前版本如果为
alpha版本,安装主题无法通过版本验证的问题。 halo-dev/halo#1705 @JohnNiang - 修复评论部分因为没有添加事务,导致批量删除评论等操作时报错的问题。 halo-dev/halo#1716 @guqing
- 修复后台点击后台 Halo Logo 进入开发者选项过快可能会导致计数为负的问题。 halo-dev/halo-admin#492 @ruibaby
- 修复后台附件列表分页之后,可能会导致无法正常更新图片 dom 导致图片显示为上一页图片的问题。 halo-dev/halo-admin#496 @ruibaby
- 修复后台分类/标签/友情链接修改表单切换修改对象之后没有清除表单验证的问题。 halo-dev/halo-admin#501 halo-dev/halo-admin#503 @Yorksh1re
- 修复当前版本如果为
alpha版本,安装主题无法通过版本验证的问题。 halo-dev/halo#1747 @JohnNiang
Dependencies
- 升级 Spring Boot 版本。 halo-dev/halo#1635 halo-dev/halo#1677 @ruibaby @JohnNiang
- 升级 Gradle 版本到 7.4。 halo-dev/halo#1697 @guqing
halo-dev/halo-admin常规依赖升级。 halo-dev/halo-admin#453 halo-dev/halo-admin#513 @ruibabyhalo-dev/halo-admin修改用于切换后台样式的 less 依赖 CDN 为 unpkg。- 后台更新 @halo-dev/editor 版本。 halo-dev/halo-admin#507 @ruibaby
相关信息
- GitHub Release:https://github.com/halo-dev/halo/releases/tag/v1.5.0
- 升级文档:https://docs.halo.run/getting-started/upgrade
- 论坛交流:https://bbs.halo.run/d/2227-halo-150

Halo 1.4.10 发布
Features
- 编辑器支持脚注语法。halo-dev/halo#1406 halo-dev/halo-admin#341
Improvements
- 优化文章字数计算。halo-dev/halo#1354
- Content Api 中的获取文章列表支持传入关键字和分类 id 筛选项。halo-dev/halo#1373
- 优化导入 Markdown 时,对多级分类的处理。halo-dev/halo#1380
Security Fixes
- 修复 Freemarker SSTI 漏洞。halo-dev/halo#1402 halo-dev/halo#1427 Thanks @LazyMaple @5wimming
Bug Fixes
- 修复在分类文章列表可以显示私密文章的问题。halo-dev/halo#1379
- 修复使用后台的小工具数据导出迁移后分类密码消失的问题。halo-dev/halo#1390
- 修复在站点初始化的时候,
全局绝对路径选项设置错误的问题。halo-dev/halo#1396 - 修复 Content Api 的文章点赞接口限流没有按照文章 id 做处理的问题。halo-dev/halo#1410
- 修复回收站的文章可以访问的问题。halo-dev/halo#1414
- 修复后台评论回复时,输入框无法输入空格的问题。halo-dev/halo-admin#322
- 修复后台菜单管理中菜单项的链接过长会导致挡住操作按钮的问题。halo-dev/halo-admin#328
- 修复后台日志管理中长文本无法换行的问题。halo-dev/halo-admin#330
- 修复后台在登录页面无法通过回车键进行登录的问题。halo-dev/halo-admin#332
Upgrade guide
https://docs.halo.run/install/upgrade

Halo 1.4.6 发布
Features
- 主题编写支持继承以及 block 特性。halo-dev/halo#1295
Improvements
- 重构初始化页面,目前分为全新安装和数据导入。从这个版本开始,数据导入将导入所有数据。halo-dev/halo-admin#296
- 优化后台菜单管理的排序拖动体验。halo-dev/halo-admin#291
- 移除后台设置项字符数的限制。halo-dev/halo#1287
Bug Fixes
- 修复后台上传组件格式限制不生效的问题。halo-dev/halo-admin#296
- 修复后台新建文章页面,点击浏览器回退键不提示保存的问题。halo-dev/halo-admin#302
- 修复后台备份无法下载的问题。halo-dev/halo#1278
- 修复 1.4.5 版本中前台页面渲染不完整的问题。halo-dev/halo#1301
- 修复上传某些格式的图片因为缩略图生成失败导致上传错误的问题。halo-dev/halo#1298
- 修复 Swagger 文档分页数据字段和实际不一致的问题。halo-dev/halo#1277
- 修复 1.4.5 版本中通过主题包升级主题失败的问题。halo-dev/halo#1284
Upgrade guide
https://docs.halo.run/install/upgrade

Halo 1.4.3 发布
随着这个版本的发布,我们的官网和文档也发生了一些变更。变更之后和 Halo 相关的站点:
- 官网:https://halo.run
- 文档:https://docs.halo.run
- 论坛:https://bbs.halo.run
- 主题仓库:https://halo.run/themes.html
Breaking changes
- 此版本不再支持 JRE 1.8,更新到此版本需要 JRE 11 或以上版本。#1184
- 移除 Redis Cache Store,如有配置该缓存方式,请先修改为其他方式再进行升级。#1190
Features
- 支持
ID 别名型文章路径类型。#1173 - 支持自定义页面的路径层级设置。#1177
- Content API 新增
日志点赞,文章上下页接口。#1176 - 支持导出所有文章为 Markdown 文件。#1199
Improvements
- 更友好的异常日志追踪。#1191
- 日志发布不再限制字数。#1203
- 优化后台菜单拖动排序体验。halo-dev/halo-admin#267
- 优化后台文章编辑页面的布局。halo-dev/halo-admin#286
Bug Fixes
- 修复定时删除回收站文章功能无效的问题。#1207
- 修复上传主题包更新主题失败的问题。#1209
- 修复自动转化文章标题为别名时,将非中文字符分割的问题。halo-dev/halo-admin#273
- 修复设置 MFA 登录校验器之后,登录页面偶发无法显示 MFA 验证码输入框的问题。halo-dev/halo-admin#276
Upgrade guide
https://docs.halo.run/install/upgrade

Halo 1.4.0 发布
Feature
- 支持静态存储重命名和修改文件内容。#819
- 所有附件列表均支持右键复制图片链接。halo-dev/halo-admin#180
- 开发者选项中的实时日志支持自动滚动到最新的日志。
- 在线下载主题支持选择分支和 release。#515 #592 #835
- 评论内容支持显示 html 文本。halo-dev/halo-admin#222
- 文章新增
wordCount字段,用于统计字数。#965 - 文章编辑支持自动将文章标题的拼音设置为别名。halo-dev/halo-admin#235
- 重构登录页面,并且支持在登录状态失效后弹出登录框,而不是直接跳转到登录页面,防止正在编辑中的文章丢失。halo-dev/halo-admin#238
- 预览草稿的时候,不再会增加访问量。#834
- Content API 支持使用文章或者页面的 slug 获取文章信息。#1044
Change
- 为部分表单添加表单验证。
- 发布文章时采用实际点击发布按钮时的时间。halo-dev/halo-admin#160
- 添加 renderer meta 标签,让部分双核浏览器强制使用新一代内核,而不是 IE 内核导致页面无法正常渲染。halo-dev/halo-admin#207
- 减弱所有动画效果。halo-dev/halo-admin#213
- 移除部分操作的吐司提示,改为直接在按钮上显示操作结果。halo-dev/halo-admin#216
- 优化大量不合理的代码。halo-dev/halo-admin#213 halo-dev/halo-admin#215
- 移除 fastjson 依赖。#871
- 重构主题目录扫描,允许当前没有激活中的主题。#869
- 移除在开发者选项中重启应用的功能。#917
- 移除 Token 不存在时抛出的异常。#962
- 优化 Markdown 导入功能。#977
- 修复文章管理页面刷新后分页显示不正确的问题。halo-dev/halo-admin#231
- 修复文件上传组件无法同时上传多个文件的问题。halo-dev/halo-admin#234
- 修复异常图片上传的时候,没有捕获异常的问题。#1025
- 优化文章编辑提示未保存弹窗的时机。halo-dev/halo-admin#242
- 移除开发者选项中修改配置文件和重启服务的功能。halo-dev/halo-admin#244
- 优化主题管理页面的布局。halo-dev/halo-admin#245
- 优化远程下载主题的体验。halo-dev/halo-admin#249
- 优化博客设置页面的布局。halo-dev/halo-admin#251
Fixed
- 修复取消全局绝对路径导致加密文章无法正确查看的问题。#785 #854
- 修复 token 无法正确失效的问题。halo-dev/halo-admin#129
- 修复附件不存在时调用删除接口抛异常的问题。#951
- 修复 content api 中查询单篇文章或页面时,没有发出浏览量增加事件的问题。#981
- 修复自动生成的文章摘要中清除了空格的问题。#1003
- 修复文章页面渲染耗时过长的问题。#1008
- 修复主题版本校验没有处理 beta 形式的版本号,从而导致无法更新或者安装主题的问题。#1011
- 修复文章管理页面刷新后分页显示不正确的问题。halo-dev/halo-admin#231
- 修复文件上传组件无法同时上传多个文件的问题。halo-dev/halo-admin#234
- 修复异常图片上传的时候,没有捕获异常的问题。#1025
- 修复退出登录和初始化引导页面的部分问题。halo-dev/halo-admin#239 halo-dev/halo-admin#240
- 修复网站备份的时候,上级目录不存在导致备份异常的问题。#1056
- 修复无法上传
tar.gz类型文件的问题。#1057 - 修复某些情况下主题设置保存失败的问题。#1070
- 修复上传附件或者主题时,由于部分系统会定时清理临时目录,导致上传失败的问题。
升级步骤
- 停止运行 Halo:
service halo stop。 - 备份数据:
cp -r ~/.halo ~/.halo.bak。 - 重命名(备份)旧运行包:
mv halo-latest.jar halo-latest.jar.bak。 - 下载新运行包:
wget https://dl.halo.run/release/halo-1.4.0.jar -O halo-latest.jar。 - 运行:
service halo start。
注意事项
- 更新前不要忘了备份数据,不管你是以什么方式部署的,都请备份
~/.halo,当然,如果你使用 docker 部署,并修改了映射路径的话,就备份你的映射路径。 - 如果有使用 CDN 全站加速,请更新完毕后,刷新全站缓存,并清空浏览器缓存。
相关链接
- https://github.com/halo-dev/halo/releases/tag/v1.4.0
- https://bbs.halo.run/d/999-halo-140
- https://halo.run/archives/halo-140-released.html

分享一个教科书式的提问方式
今天,收到一封教科书式的提问邮件。坦率地讲,这还是我第一次收到这种逻辑清晰、信息提供到位的提问。所以非常激动,特意水一篇文来和大家分享一下。
邮件原文
Ryan Wang:
你好,我是刚开始使用Halo的程序员。Halo是我接触过最优秀的作品之一,难以置信能够遇到如此优秀的博客系统,在我第一次了解到它的时候,我就决定用它来搭建一个博客。
在我安装好,顺利运行Halo之后,我迫不及待的尝试了几个主题,最后我觉得Journal非常符合我的个人喜好,同时惊喜的发现原来您也在用这款主题作为自己的主页。但随之而来我在这个主题上遇到了一个小问题。
【问题】
当在移动端显示时,博客主题的div向左偏移了一部分,同时最上方的折叠菜单栏无法正确显示,导致上下的中轴线不一样,页面总是左右滑动。
我尝试了自己调试,但在chrome上调整为手机显示后,却是正常,同时这个问题也不是在所有手机上都有问题,我使用了多款手机,发现个别手机上可以正常显示。
【截图】
以下截图为您和主题作者主页的正常截图,标题可居中显示,同时在下滑后可以显示在在折叠菜单栏。
![]()
以下是我的博客异常显示截图,标题不居中,下滑后折叠菜单栏也无法正确显示。
-
图1的空白处还有一个隐藏的链接,也是链接到我的主页,标题div整体偏左。
-
图2我将标题div边界显示出来了,确实偏左。
-
图3下滑后折叠菜单栏没有置顶显示。
-
图4是在chrome的调试模式下能够正确显示。
![]()
![]()
【资源】
-
Halo版本:v1.1.1
-
Journal主题:当前halo-dev/halo-theme-Journal仓库master最新版本,commit id:2747364e50880d44ae4ac8fcb1513352aa199039
-
尝试过的浏览器:
-
iphone xs: safari、chrome、微信内置浏览器 [均异常]
-
iPhone xs max: safari [异常]
-
Huawei mate30 pro: 自带浏览器、百度浏览器 [均异常]
-
Huawei p20: chrome [正常]
-
-
主页地址:http://xxxx(文章中就不写地址了)
由于我对前端相关技术非常不熟悉,自己调试很久都无法使其正确,特此咨询博主是如何解决的,若博主有时间,可否看下之前是否也遇到过类似问题,或是做过何种修改使其正确显示了。
期待您的回信,十分感谢!!
Your fans:xxxx(文章中就不写名字了)
2020年1月5日
为什么写得好?
邮件格式标准
虽说现在很多人(包括我),在发邮件的时候都不会太刻意在乎邮件格式,但是这种格式我看起来就是舒服,也有了阅读下去的欲望。
有问题简述
可以看到,上面的 【问题】 板块中,他有把问题简写为一小段,并特意标注,这样的话我就有可能能一下子了解问题所在,在我脑子里搜索一下是否有遇到过类似的问题。
有问题详细描述
紧接着 问题简述,随后就是问题的详细描述,附有多张截图。并把所有导致的问题全部描述了一遍。
有自己尝试解决问题
从上面的描述就可以知道,他有在桌面版的 Chrome 中调试过主题,也在多款手机的浏览器中调试过,说明他有尝试通过自己解决问题,比如:是否和浏览器有关?是否和设备有关?都很显而易见。
提供了环境信息
他在最后面提供了 Halo 的版本,主题的版本以及 commit id。这是最方便我们定位问题的信息,有可能在某些情况,我们已经在最新版本修复了此问题,所以当你没有提供这些信息的时候,我们往往会问你软件的版本或者主题的版本。
提供了线上地址
这就不用我多说了,这是最直接了当方便我们调试的信息。
总结
我就希望以后有同学抛出问题的时候,能多附带一些信息,比如详细日志,线上地址等等。真的,不为其他的,就为了不耽搁我们双方的时间,从而更加迅猛的帮你解决问题。某些时候真的不是不想解决你们遇到的问题,而且你们给出的信息实在过少,难以判断,而往往这时候我们又要向你问一些详细情况,这样一去一来不就耽搁时间了吗?
另外,感谢这位朋友愿意提供该邮件供我水这篇文章,我认为比我以前水的一些文章有价值多了,感谢。

优雅的让 Halo 支持 webp 图片输出
原文地址:https://halo.run/archives/halo-and-webp
是什么
WebP的有损压缩算法是基于VP8视频格式的帧内编码[17],并以RIFF作为容器格式。[2] 因此,它是一个具有八位色彩深度和以1:2的比例进行色度子采样的亮度-色度模型(YCbCr 4:2:0)的基于块的转换方案。[18] 不含内容的情况下,RIFF容器要求只需20字节的开销,依然能保存额外的 元数据(metadata)。[2] WebP图像的边长限制为16383像素。
在 WebP 的官网中,我们可以发现 Google 是这样宣传 WebP 的:
WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index.
简单来说,WebP 图片格式的存在,让我们在 WebP 上展示的图片体积可以有较大幅度的缩小,也就带来了加载性能的提升。(摘自 https://nova.moe/re-introduce-webp-server)
怎么做
那么如何优雅的在不替换图片地址的情况下,将图片转为 webp 格式然后输出呢?
这时候就可以使用 webp-sh 组织最新开源的 webp_server_go 了,它的大概原理就是:当我们请求一张图片的时候使用 web 代理工具转发到 webp_server_go 应用进行处理,处理完成之后返回 webp 格式的图片,并且会保留处理后的图片以供后面的访问。
目前大部分主流浏览器都已经支持了 webp 图片的显示,除了 Safari,但是不必担心,webp_server_go 会自动判断请求来源是否为 Safari,如果是,那么会返回原图。
下面将提供两种 web 服务器的代理方法。
此教程以 CentOS 7.x 为例,其他发行版本大同小异。另外,此教程只针对于 Halo,其他 web 程序可能在 config.json 部分有所不同,建议参考仓库的 README。
部署 webp_server_go
在 仓库 的 README 中已经大致讲解了部署方法,在这里针对 Halo 详细说明一下。
下载官方编译好的 webp_server_go 二进制文件
如果你有能力,也可以自行编译。
新建一个存放二进制文件和 config.json 文件的目录(可自定义):
mkdir /opt/webps
cd /opt/webps
下载二进制文件(最新版本请访问 releases):
wget https://github.com/webp-sh/webp_server_go/releases/download/0.1.0/webp-server-linux-amd64 -O webp-server
给予执行权限:
chmod +x webp-server
创建 config.json
{
"HOST": "127.0.0.1",
"PORT": "3333",
"QUALITY": "80",
"IMG_PATH": "/root/.halo",
"EXHAUST_PATH": "/root/.halo/cache",
"ALLOWED_TYPES": ["jpg","png","jpeg"]
}
参数解释:
-
HOST:一般不修改。
-
PORT:webp_server_go 的运行端口。
-
QUALITY:转换质量,默认为 80%。
-
IMG_PATH:固定格式,/运行 Halo 的用户名/.halo
-
EXHAUST_PATH:固定格式,/运行 Halo 的用户名/.halo/cache
-
ALLOWED_TYPES:需要转换的格式
使用 systemd 进行状态管理
创建 service 文件:
vim /etc/systemd/system/webps.service
写入:
[Unit]
Description=WebP Server
Documentation=https://github.com/n0vad3v/webp_server_go
After=nginx.target
[Service]
Type=simple
StandardError=journal
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=/opt/webps
ExecStart=/opt/webps/webp-server --config /opt/webps/config.json
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target
需要注意的是,ExecStart 命令中的程序路径和配置文件路径一定要正确,结合你的实际情况填写。
然后执行:
systemctl daemon-reload
systemctl enable webps.service
systemctl start webps.service
查看运行状态:
systemctl status webps.service
如果没有问题,那么会输出以下日志:
WebP Server is running at 127.0.0.1:3333
使用 Nginx 进行代理
如果你的 Halo 是使用 Nginx 反向代理的话。
修改 halo.conf
在 server 节点添加:
location ^~ /upload/ {
proxy_pass http://127.0.0.1:3333;
proxy_set_header X-Real-IP $remote_addr;
proxy_hide_header X-Powered-By;
proxy_set_header HOST $http_host;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
}
重载 Nginx 配置:
# 检查配置文件是否有问题
nginx -t
nginx -s reload
使用 Caddy 进行代理
如果你的 Halo 是使用 Caddy 反向代理的话。
修改 Caddyfile
在你域名节点下添加:
proxy /upload/ localhost:3333 {
transparent
}
重启 Caddy:
service caddy restart
教程完毕,下面讲一下如何验证是否生效。
验证是否生效
![]()
注意看 Type 列,图片的返回格式已经变成了 webp,而且图片大小已经远远降低,那么说明你的配置已经成功了。have fun!
如果用的开心,请关注一下 https://github.com/webp-sh/webp_server_go 哦!另外,他们还有其他语言的版本,请查看 https://github.com/webp-sh。
链接:

Halo v1.0 正式发布
前言
Halo 从去年 5 月开源以来,广受小伙伴们的喜爱,在此非常感谢使用 Halo 发表博客的小伙伴们。
今年,在 @JohnNiang 的帮助下,我们几乎完全重写了 Halo,然后 1.0 正式版就发布了。在此,非常感谢 @JohnNiang 的加入以及他做出的贡献。再到后面,我们公开了 admin api 之后,@雨季不再来 使用了 Flutter 为 Halo 开发了管理端的 APP。相信以后越来越多人加入之后,Halo 会变得更好。希望大家会喜欢。
主要特性
- 拥有使用 Vue 开发的后台管理,体验升级,但是并不需要独立部署,启动 Halo 即可。
- 拥有 Restful 风格的 Content api,你可以用于开发单页面主题,微信小程序等。
- 拥有 Restful 风格的 Admin api,你可以用于开发桌面管理客户端,管理 App(已有) 等。
- 拥有使用 Flutter 开发的管理端 App,支持 Android 和 iOS,随时随地发表你的想法!感谢@雨季不再来。
- 拥有独立的评论插件,使用 Vue 开发,只需在页面引入构建好的 JS 文件即可,完美地和主题相结合。
- 支持多主题。另外,还支持在线下载主题以及更新主题。
- 支持在线修改主题文件内容,无需在本地修改然后上传。
- 十分友好的主题开发体验,支持自定义配置。(主题开发文档正在开发中)。
- 功能强大的附件管理,同时支持本地上传,又拍云/七牛云/阿里云等云存储,另外,还支持 SM.MS 图床(非常感谢 SM.MS,请大家善用该服务哦)。
- 自带友情链接管理,图库管理(给爱摄影的小伙伴们)。
- 支持自定义页面。
- 支持 Markdown 文档导入,顺带解析 FrontMatter。
- 支持日志功能,类似于 QQ 空间的说说,亦或者微博。同时支持微信发布日志(后续计划)。
- 还有…
相关链接
- Halo 开源地址:https://github.com/halo-dev/halo
- Web 管理端:https://github.com/halo-dev/halo-admin
- 管理端 APP:https://github.com/halo-dev/halo-app
- 独立评论插件:https://github.com/halo-dev/halo-comment
- 主题仓库:https://halo.run/theme
- 交流论坛:https://bbs.halo.run
有喜欢的同学可以点个 star 哦。有任何问题可以去 Github issues 或者 https://bbs.halo.run 。
预览图
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()

Halo v1.0 is coming
非常感谢大家对 Halo 的支持,经过接近两个月的重构,Halo v1.0 就快要发布了,非常感谢 @JohnNiang 做出的贡献。希望大家会喜欢新版的 Halo v1.0。
组织地址:https://github.com/halo-dev
废话少说,先看东西
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()






